From b158645575a569bd46de15b42355940501902166 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 23 Apr 2020 00:09:41 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../projects/settings/ci_cd/show/index.js | 4 - .../clusters/applications_controller.rb | 2 +- .../projects/deploy_tokens_controller.rb | 2 +- .../projects/settings/ci_cd_controller.rb | 38 ------ .../settings/repository_controller.rb | 51 +++++++- app/helpers/environments_helper.rb | 3 +- app/models/alert_management/alert.rb | 62 +++++++++ app/models/clusters/applications/fluentd.rb | 20 ++- app/models/internal_id_enums.rb | 2 +- app/models/issue.rb | 1 + app/models/project.rb | 4 +- app/serializers/cluster_application_entity.rb | 2 + .../clusters/applications/base_service.rb | 20 ++- .../projects/settings/ci_cd/show.html.haml | 3 - .../settings/repository/show.html.haml | 2 + ...base-table-for-alert-management-alerts.yml | 5 + .../unreleased/ph-214480-copyUploadLinks.yml | 5 + ...lete-resource-milestone-events-columns.yml | 5 + ...ate_fluentd_model_to_include_log_flags.yml | 5 + config/routes/project.rb | 4 +- ...4_add_group_id_to_vulnerability_exports.rb | 10 ++ ...d_index_and_fk_to_vulnerability_exports.rb | 20 +++ ..._project_index_on_vulnerability_exports.rb | 21 ++++ ...17044453_create_alert_management_alerts.rb | 44 +++++++ ...oreign_keys_for_alert_management_alerts.rb | 19 +++ ..._columns_from_resource_milestone_events.rb | 11 ++ db/structure.sql | 72 ++++++++++- doc/administration/scaling/index.md | 6 + lib/banzai/filter/upload_link_filter.rb | 4 + lib/gitlab/jira_import/issue_serializer.rb | 48 +------ lib/gitlab/jira_import/metadata_collector.rb | 60 +++++++++ locale/gitlab.pot | 3 + qa/Gemfile | 2 +- qa/Gemfile.lock | 4 +- qa/qa/page/project/issue/index.rb | 21 ++++ qa/qa/page/project/settings/ci_cd.rb | 10 -- qa/qa/page/project/settings/repository.rb | 10 ++ qa/qa/resource/deploy_token.rb | 14 +-- .../settings/ci_cd_controller_spec.rb | 80 ------------ .../settings/repository_controller_spec.rb | 80 ++++++++++++ spec/factories/alert_management/alerts.rb | 34 +++++ spec/factories/clusters/applications/helm.rb | 2 + spec/features/markdown/copy_as_gfm_spec.rb | 6 + .../projects/settings/ci_cd_settings_spec.rb | 16 --- .../settings/repository_settings_spec.rb | 14 +++ ...ser_sees_revoke_deploy_token_modal_spec.rb | 2 +- spec/fixtures/api/schemas/cluster_status.json | 2 + .../banzai/filter/upload_link_filter_spec.rb | 17 +++ spec/lib/gitlab/import_export/all_models.yml | 2 + .../jira_import/issue_serializer_spec.rb | 65 +--------- .../jira_import/metadata_collector_spec.rb | 110 ++++++++++++++++ spec/models/alert_management/alert_spec.rb | 119 ++++++++++++++++++ .../clusters/applications/fluentd_spec.rb | 36 +++++- spec/models/issue_spec.rb | 1 + spec/models/project_spec.rb | 1 + spec/routing/project_routing_spec.rb | 5 +- .../cluster_application_entity_spec.rb | 12 ++ 57 files changed, 917 insertions(+), 306 deletions(-) create mode 100644 app/models/alert_management/alert.rb create mode 100644 changelogs/unreleased/214518-database-table-for-alert-management-alerts.yml create mode 100644 changelogs/unreleased/ph-214480-copyUploadLinks.yml create mode 100644 changelogs/unreleased/remove-obsolete-resource-milestone-events-columns.yml create mode 100644 changelogs/unreleased/update_fluentd_model_to_include_log_flags.yml create mode 100644 db/migrate/20200414112444_add_group_id_to_vulnerability_exports.rb create mode 100644 db/migrate/20200414114611_add_group_id_index_and_fk_to_vulnerability_exports.rb create mode 100644 db/migrate/20200414115801_change_project_index_on_vulnerability_exports.rb create mode 100644 db/migrate/20200417044453_create_alert_management_alerts.rb create mode 100644 db/migrate/20200421233150_add_foreign_keys_for_alert_management_alerts.rb create mode 100644 db/post_migrate/20200401091051_remove_reference_columns_from_resource_milestone_events.rb create mode 100644 lib/gitlab/jira_import/metadata_collector.rb create mode 100644 spec/factories/alert_management/alerts.rb create mode 100644 spec/lib/gitlab/jira_import/metadata_collector_spec.rb create mode 100644 spec/models/alert_management/alert_spec.rb diff --git a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js index 7f865f4cfb6..aeeef40fc6e 100644 --- a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js +++ b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js @@ -3,7 +3,6 @@ import SecretValues from '~/behaviors/secret_values'; import AjaxVariableList from '~/ci_variable_list/ajax_variable_list'; import registrySettingsApp from '~/registry/settings/registry_settings_bundle'; import initVariableList from '~/ci_variable_list'; -import DueDateSelectors from '~/due_date_select'; import initDeployKeys from '~/deploy_keys'; document.addEventListener('DOMContentLoaded', () => { @@ -41,9 +40,6 @@ document.addEventListener('DOMContentLoaded', () => { autoDevOpsExtraSettings.classList.toggle('hidden', !target.checked); }); - // eslint-disable-next-line no-new - new DueDateSelectors(); - registrySettingsApp(); initDeployKeys(); }); diff --git a/app/controllers/clusters/applications_controller.rb b/app/controllers/clusters/applications_controller.rb index de14bd319e0..c533fe007d7 100644 --- a/app/controllers/clusters/applications_controller.rb +++ b/app/controllers/clusters/applications_controller.rb @@ -47,7 +47,7 @@ class Clusters::ApplicationsController < Clusters::BaseController end def cluster_application_params - params.permit(:application, :hostname, :pages_domain_id, :email, :stack, :modsecurity_enabled, :modsecurity_mode, :host, :port, :protocol) + params.permit(:application, :hostname, :pages_domain_id, :email, :stack, :modsecurity_enabled, :modsecurity_mode, :host, :port, :protocol, :waf_log_enabled, :cilium_log_enabled) end def cluster_application_destroy_params diff --git a/app/controllers/projects/deploy_tokens_controller.rb b/app/controllers/projects/deploy_tokens_controller.rb index 4a70424ec01..830b1f4fe4a 100644 --- a/app/controllers/projects/deploy_tokens_controller.rb +++ b/app/controllers/projects/deploy_tokens_controller.rb @@ -7,6 +7,6 @@ class Projects::DeployTokensController < Projects::ApplicationController @token = @project.deploy_tokens.find(params[:id]) @token.revoke! - redirect_to project_settings_ci_cd_path(project, anchor: 'js-deploy-tokens') + redirect_to project_settings_repository_path(project, anchor: 'js-deploy-tokens') end end diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb index c7cd9649dac..c4d291e8634 100644 --- a/app/controllers/projects/settings/ci_cd_controller.rb +++ b/app/controllers/projects/settings/ci_cd_controller.rb @@ -48,33 +48,6 @@ module Projects redirect_to namespace_project_settings_ci_cd_path end - def create_deploy_token - result = Projects::DeployTokens::CreateService.new(@project, current_user, deploy_token_params).execute - @new_deploy_token = result[:deploy_token] - - if result[:status] == :success - respond_to do |format| - format.json do - # IMPORTANT: It's a security risk to expose the token value more than just once here! - json = API::Entities::DeployTokenWithToken.represent(@new_deploy_token).as_json - render json: json, status: result[:http_status] - end - format.html do - flash.now[:notice] = s_('DeployTokens|Your new project deploy token has been created.') - render :show - end - end - else - respond_to do |format| - format.json { render json: { message: result[:message] }, status: result[:http_status] } - format.html do - flash.now[:alert] = result[:message] - render :show - end - end - end - end - private def update_params @@ -93,10 +66,6 @@ module Projects end end - def deploy_token_params - params.require(:deploy_token).permit(:name, :expires_at, :read_repository, :read_registry, :write_registry, :username) - end - def run_autodevops_pipeline(service) return unless service.run_auto_devops_pipeline? @@ -116,7 +85,6 @@ module Projects def define_variables define_runners_variables define_ci_variables - define_deploy_token_variables define_triggers_variables define_badges_variables define_auto_devops_variables @@ -168,12 +136,6 @@ module Projects @auto_devops = @project.auto_devops || ProjectAutoDevops.new end - def define_deploy_token_variables - @deploy_tokens = @project.deploy_tokens.active - - @new_deploy_token = DeployToken.new - end - def define_deploy_keys @deploy_keys = DeployKeysPresenter.new(@project, current_user: current_user) end diff --git a/app/controllers/projects/settings/repository_controller.rb b/app/controllers/projects/settings/repository_controller.rb index a1f88c73649..68bab952217 100644 --- a/app/controllers/projects/settings/repository_controller.rb +++ b/app/controllers/projects/settings/repository_controller.rb @@ -4,7 +4,10 @@ module Projects module Settings class RepositoryController < Projects::ApplicationController before_action :authorize_admin_project! - before_action :remote_mirror, only: [:show] + before_action :define_variables, only: [:create_deploy_token] + before_action do + push_frontend_feature_flag(:ajax_new_deploy_token, @project) + end def show render_show @@ -24,15 +27,47 @@ module Projects redirect_to project_settings_repository_path(project) end + def create_deploy_token + result = Projects::DeployTokens::CreateService.new(@project, current_user, deploy_token_params).execute + @new_deploy_token = result[:deploy_token] + + if result[:status] == :success + respond_to do |format| + format.json do + # IMPORTANT: It's a security risk to expose the token value more than just once here! + json = API::Entities::DeployTokenWithToken.represent(@new_deploy_token).as_json + render json: json, status: result[:http_status] + end + format.html do + flash.now[:notice] = s_('DeployTokens|Your new project deploy token has been created.') + render :show + end + end + else + respond_to do |format| + format.json { render json: { message: result[:message] }, status: result[:http_status] } + format.html do + flash.now[:alert] = result[:message] + render :show + end + end + end + end + private def render_show - define_protected_refs - remote_mirror + define_variables render 'show' end + def define_variables + define_deploy_token_variables + define_protected_refs + remote_mirror + end + # rubocop: disable CodeReuse/ActiveRecord def define_protected_refs @protected_branches = @project.protected_branches.order(:name).page(params[:page]) @@ -51,6 +86,10 @@ module Projects @remote_mirror = project.remote_mirrors.first_or_initialize end + def deploy_token_params + params.require(:deploy_token).permit(:name, :expires_at, :read_repository, :read_registry, :write_registry, :username) + end + def access_levels_options { create_access_levels: levels_for_dropdown, @@ -74,6 +113,12 @@ module Projects { open_branches: ProtectableDropdown.new(@project, :branches).hash } end + def define_deploy_token_variables + @deploy_tokens = @project.deploy_tokens.active + + @new_deploy_token ||= DeployToken.new + end + def load_gon_index gon.push(protectable_tags_for_dropdown) gon.push(protectable_branches_for_dropdown) diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb index 3368fc7aa86..21b2111baf7 100644 --- a/app/helpers/environments_helper.rb +++ b/app/helpers/environments_helper.rb @@ -2,7 +2,6 @@ module EnvironmentsHelper include ActionView::Helpers::AssetUrlHelper - prepend_if_ee('::EE::EnvironmentsHelper') # rubocop: disable Cop/InjectEnterpriseEditionModule def environments_list_data { @@ -65,3 +64,5 @@ module EnvironmentsHelper can?(current_user, :destroy_environment, environment) end end + +EnvironmentsHelper.prepend_if_ee('::EE::EnvironmentsHelper') diff --git a/app/models/alert_management/alert.rb b/app/models/alert_management/alert.rb new file mode 100644 index 00000000000..494f8d361d0 --- /dev/null +++ b/app/models/alert_management/alert.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +module AlertManagement + class Alert < ApplicationRecord + include AtomicInternalId + include ShaAttribute + + belongs_to :project + belongs_to :issue, optional: true + has_internal_id :iid, scope: :project, init: ->(s) { s.project.alert_management_alerts.maximum(:iid) } + + self.table_name = 'alert_management_alerts' + + sha_attribute :fingerprint + + HOSTS_MAX_LENGTH = 255 + + validates :title, length: { maximum: 200 }, presence: true + validates :description, length: { maximum: 1_000 } + validates :service, length: { maximum: 100 } + validates :monitoring_tool, length: { maximum: 100 } + validates :project, presence: true + validates :events, presence: true + validates :severity, presence: true + validates :status, presence: true + validates :started_at, presence: true + validates :fingerprint, uniqueness: { scope: :project }, allow_blank: true + validate :hosts_length + + enum severity: { + critical: 0, + high: 1, + medium: 2, + low: 3, + info: 4, + unknown: 5 + } + + enum status: { + triggered: 0, + acknowledged: 1, + resolved: 2, + ignored: 3 + } + + def fingerprint=(value) + if value.blank? + super(nil) + else + super(Digest::SHA1.hexdigest(value.to_s)) + end + end + + private + + def hosts_length + return unless hosts + + errors.add(:hosts, "hosts array is over #{HOSTS_MAX_LENGTH} chars") if hosts.join.length > HOSTS_MAX_LENGTH + end + end +end diff --git a/app/models/clusters/applications/fluentd.rb b/app/models/clusters/applications/fluentd.rb index a33b1e39ace..3fd6e870edc 100644 --- a/app/models/clusters/applications/fluentd.rb +++ b/app/models/clusters/applications/fluentd.rb @@ -4,6 +4,7 @@ module Clusters module Applications class Fluentd < ApplicationRecord VERSION = '2.4.0' + CILIUM_CONTAINER_NAME = 'cilium-monitor' self.table_name = 'clusters_applications_fluentd' @@ -18,6 +19,8 @@ module Clusters enum protocol: { tcp: 0, udp: 1 } + validate :has_at_least_one_log_enabled? + def chart 'stable/fluentd' end @@ -39,6 +42,12 @@ module Clusters private + def has_at_least_one_log_enabled? + if !waf_log_enabled && !cilium_log_enabled + errors.add(:base, _("At least one logging option is required to be enabled")) + end + end + def content_values YAML.load_file(chart_values_file).deep_merge!(specification) end @@ -62,7 +71,7 @@ module Clusters program fluentd hostname ${kubernetes_host} protocol #{protocol} - packet_size 65535 + packet_size 131072 @@ -85,7 +94,7 @@ module Clusters @type tail @id in_tail_container_logs - path /var/log/containers/*#{Ingress::MODSECURITY_LOG_CONTAINER_NAME}*.log + path #{path_to_logs} pos_file /var/log/fluentd-containers.log.pos tag kubernetes.* read_from_head true @@ -96,6 +105,13 @@ module Clusters EOF end + + def path_to_logs + path = [] + path << "/var/log/containers/*#{Ingress::MODSECURITY_LOG_CONTAINER_NAME}*.log" if waf_log_enabled + path << "/var/log/containers/*#{CILIUM_CONTAINER_NAME}*.log" if cilium_log_enabled + path.join(',') + end end end end diff --git a/app/models/internal_id_enums.rb b/app/models/internal_id_enums.rb index 1b7ad00e58b..84f450fc3a8 100644 --- a/app/models/internal_id_enums.rb +++ b/app/models/internal_id_enums.rb @@ -3,7 +3,7 @@ module InternalIdEnums def self.usage_resources # when adding new resource, make sure it doesn't conflict with EE usage_resources - { issues: 0, merge_requests: 1, deployments: 2, milestones: 3, epics: 4, ci_pipelines: 5, operations_feature_flags: 6, operations_user_lists: 7 } + { issues: 0, merge_requests: 1, deployments: 2, milestones: 3, epics: 4, ci_pipelines: 5, operations_feature_flags: 6, operations_user_lists: 7, alert_management_alerts: 8 } end end diff --git a/app/models/issue.rb b/app/models/issue.rb index cdd7429bc58..41565ab8694 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -48,6 +48,7 @@ class Issue < ApplicationRecord has_many :sent_notifications, as: :noteable has_one :sentry_issue + has_one :alert_management_alert, class_name: 'AlertManagement::Alert' accepts_nested_attributes_for :sentry_issue diff --git a/app/models/project.rb b/app/models/project.rb index ab5d233060e..d9c09fe8ace 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -257,6 +257,8 @@ class Project < ApplicationRecord has_many :prometheus_alert_events, inverse_of: :project has_many :self_managed_prometheus_alert_events, inverse_of: :project + has_many :alert_management_alerts, class_name: 'AlertManagement::Alert', 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 # here. @@ -2368,7 +2370,7 @@ class Project < ApplicationRecord end def deploy_token_create_url(opts = {}) - Gitlab::Routing.url_helpers.create_deploy_token_project_settings_ci_cd_path(self, opts) + Gitlab::Routing.url_helpers.create_deploy_token_project_settings_repository_path(self, opts) end def deploy_token_revoke_url_for(token) diff --git a/app/serializers/cluster_application_entity.rb b/app/serializers/cluster_application_entity.rb index 85a40f1f5cb..32b759b9628 100644 --- a/app/serializers/cluster_application_entity.rb +++ b/app/serializers/cluster_application_entity.rb @@ -19,4 +19,6 @@ class ClusterApplicationEntity < Grape::Entity expose :host, if: -> (e, _) { e.respond_to?(:host) } expose :port, if: -> (e, _) { e.respond_to?(:port) } expose :protocol, if: -> (e, _) { e.respond_to?(:protocol) } + expose :waf_log_enabled, if: -> (e, _) { e.respond_to?(:waf_log_enabled) } + expose :cilium_log_enabled, if: -> (e, _) { e.respond_to?(:cilium_log_enabled) } end diff --git a/app/services/clusters/applications/base_service.rb b/app/services/clusters/applications/base_service.rb index 86b48b5228d..39a2d6bf758 100644 --- a/app/services/clusters/applications/base_service.rb +++ b/app/services/clusters/applications/base_service.rb @@ -5,6 +5,8 @@ module Clusters class BaseService InvalidApplicationError = Class.new(StandardError) + FLUENTD_KNOWN_ATTRS = %i[host protocol port waf_log_enabled cilium_log_enabled].freeze + attr_reader :cluster, :current_user, :params def initialize(cluster, user, params = {}) @@ -35,17 +37,7 @@ module Clusters application.modsecurity_mode = params[:modsecurity_mode] || 0 end - if application.has_attribute?(:host) - application.host = params[:host] - end - - if application.has_attribute?(:protocol) - application.protocol = params[:protocol] - end - - if application.has_attribute?(:port) - application.port = params[:port] - end + apply_fluentd_related_attributes(application) if application.respond_to?(:oauth_application) application.oauth_application = create_oauth_application(application, request) @@ -111,6 +103,12 @@ module Clusters ::Applications::CreateService.new(current_user, oauth_application_params).execute(request) end + + def apply_fluentd_related_attributes(application) + FLUENTD_KNOWN_ATTRS.each do |attr| + application[attr] = params[attr] if application.has_attribute?(attr) + end + end end end end diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml index c0f60b5f3b1..4c9de58cc01 100644 --- a/app/views/projects/settings/ci_cd/show.html.haml +++ b/app/views/projects/settings/ci_cd/show.html.haml @@ -4,7 +4,6 @@ - expanded = expanded_by_default? - general_expanded = @project.errors.empty? ? expanded : true -- deploy_token_description = s_('DeployTokens|Deploy tokens allow access to your repository and registry images.') %section.settings#js-general-pipeline-settings.no-animate{ class: ('expanded' if general_expanded) } .settings-header @@ -52,8 +51,6 @@ .settings-content = render 'ci/variables/index', save_endpoint: project_variables_path(@project) -= render "shared/deploy_tokens/index", group_or_project: @project, description: deploy_token_description - = render @deploy_keys %section.settings.no-animate#js-pipeline-triggers{ class: ('expanded' if expanded) } diff --git a/app/views/projects/settings/repository/show.html.haml b/app/views/projects/settings/repository/show.html.haml index 5bf92d32474..77606bfea42 100644 --- a/app/views/projects/settings/repository/show.html.haml +++ b/app/views/projects/settings/repository/show.html.haml @@ -1,6 +1,7 @@ - breadcrumb_title _("Repository Settings") - page_title _("Repository") - @content_class = "limit-container-width" unless fluid_layout +- deploy_token_description = s_('DeployTokens|Deploy tokens allow access to your repository and registry images.') = render "projects/default_branch/show" = render_if_exists "projects/push_rules/index" @@ -11,6 +12,7 @@ -# Those are used throughout the actual views. These `shared` views are then -# reused in EE. = render "projects/settings/repository/protected_branches" += render "shared/deploy_tokens/index", group_or_project: @project, description: deploy_token_description = render "projects/cleanup/show" = render_if_exists 'shared/promotions/promote_repository_features' diff --git a/changelogs/unreleased/214518-database-table-for-alert-management-alerts.yml b/changelogs/unreleased/214518-database-table-for-alert-management-alerts.yml new file mode 100644 index 00000000000..f368c5d858f --- /dev/null +++ b/changelogs/unreleased/214518-database-table-for-alert-management-alerts.yml @@ -0,0 +1,5 @@ +--- +title: Add table for Alert Management alerts +merge_request: 29864 +author: +type: added diff --git a/changelogs/unreleased/ph-214480-copyUploadLinks.yml b/changelogs/unreleased/ph-214480-copyUploadLinks.yml new file mode 100644 index 00000000000..cd8313f58de --- /dev/null +++ b/changelogs/unreleased/ph-214480-copyUploadLinks.yml @@ -0,0 +1,5 @@ +--- +title: Fixed copy as GFM not copying upload links +merge_request: 29683 +author: +type: fixed diff --git a/changelogs/unreleased/remove-obsolete-resource-milestone-events-columns.yml b/changelogs/unreleased/remove-obsolete-resource-milestone-events-columns.yml new file mode 100644 index 00000000000..bc1c9e2fb59 --- /dev/null +++ b/changelogs/unreleased/remove-obsolete-resource-milestone-events-columns.yml @@ -0,0 +1,5 @@ +--- +title: Remove obsolete columns from resource_milestone_events +merge_request: 28536 +author: +type: other diff --git a/changelogs/unreleased/update_fluentd_model_to_include_log_flags.yml b/changelogs/unreleased/update_fluentd_model_to_include_log_flags.yml new file mode 100644 index 00000000000..8d38f8f794c --- /dev/null +++ b/changelogs/unreleased/update_fluentd_model_to_include_log_flags.yml @@ -0,0 +1,5 @@ +--- +title: Update Fluentd model to support multiple logs +merge_request: 29458 +author: +type: changed diff --git a/config/routes/project.rb b/config/routes/project.rb index bcd2fbf4151..6e5784a8f92 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -73,7 +73,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do resource :ci_cd, only: [:show, :update], controller: 'ci_cd' do post :reset_cache put :reset_registration_token - post :create_deploy_token, path: 'deploy_token/create' + post :create_deploy_token, path: 'deploy_token/create', to: 'repository#create_deploy_token' end resource :operations, only: [:show, :update] do @@ -87,7 +87,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do resource :repository, only: [:show], controller: :repository do # TODO: Removed this "create_deploy_token" route after change was made in app/helpers/ci_variables_helper.rb:14 # See MR comment for more detail: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27059#note_311585356 - post :create_deploy_token, path: 'deploy_token/create', to: 'ci_cd#create_deploy_token' + post :create_deploy_token, path: 'deploy_token/create' post :cleanup end end diff --git a/db/migrate/20200414112444_add_group_id_to_vulnerability_exports.rb b/db/migrate/20200414112444_add_group_id_to_vulnerability_exports.rb new file mode 100644 index 00000000000..fad63d53a81 --- /dev/null +++ b/db/migrate/20200414112444_add_group_id_to_vulnerability_exports.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class AddGroupIdToVulnerabilityExports < ActiveRecord::Migration[6.0] + DOWNTIME = false + + def change + add_column :vulnerability_exports, :group_id, :integer + change_column_null :vulnerability_exports, :project_id, true + end +end diff --git a/db/migrate/20200414114611_add_group_id_index_and_fk_to_vulnerability_exports.rb b/db/migrate/20200414114611_add_group_id_index_and_fk_to_vulnerability_exports.rb new file mode 100644 index 00000000000..a3e60b87857 --- /dev/null +++ b/db/migrate/20200414114611_add_group_id_index_and_fk_to_vulnerability_exports.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class AddGroupIdIndexAndFkToVulnerabilityExports < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + GROUP_ID_INDEX_NAME = 'index_vulnerability_exports_on_group_id_not_null' + + disable_ddl_transaction! + + def up + add_concurrent_index(:vulnerability_exports, :group_id, where: 'group_id IS NOT NULL', name: GROUP_ID_INDEX_NAME) + add_concurrent_foreign_key(:vulnerability_exports, :namespaces, column: :group_id) + end + + def down + remove_foreign_key(:vulnerability_exports, column: :group_id) + remove_concurrent_index_by_name(:vulnerability_exports, GROUP_ID_INDEX_NAME) + end +end diff --git a/db/migrate/20200414115801_change_project_index_on_vulnerability_exports.rb b/db/migrate/20200414115801_change_project_index_on_vulnerability_exports.rb new file mode 100644 index 00000000000..e669faa7fca --- /dev/null +++ b/db/migrate/20200414115801_change_project_index_on_vulnerability_exports.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class ChangeProjectIndexOnVulnerabilityExports < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + OLD_INDEX_NAME = 'index_vulnerability_exports_on_project_id_and_id' + NEW_INDEX_NAME = 'index_vulnerability_exports_on_project_id_not_null' + + disable_ddl_transaction! + + def up + add_concurrent_index(:vulnerability_exports, :project_id, where: 'project_id IS NOT NULL', name: NEW_INDEX_NAME) + remove_concurrent_index_by_name(:vulnerability_exports, OLD_INDEX_NAME) + end + + def down + add_concurrent_index(:vulnerability_exports, [:project_id, :id], unique: true, name: OLD_INDEX_NAME) + remove_concurrent_index_by_name(:vulnerability_exports, NEW_INDEX_NAME) + end +end diff --git a/db/migrate/20200417044453_create_alert_management_alerts.rb b/db/migrate/20200417044453_create_alert_management_alerts.rb new file mode 100644 index 00000000000..6221eeeb24b --- /dev/null +++ b/db/migrate/20200417044453_create_alert_management_alerts.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +class CreateAlertManagementAlerts < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + unless table_exists?(:alert_management_alerts) + create_table :alert_management_alerts do |t| + t.timestamps_with_timezone + t.datetime_with_timezone :started_at, null: false + t.datetime_with_timezone :ended_at + t.integer :events, default: 1, null: false + t.integer :iid, null: false + t.integer :severity, default: 0, null: false, limit: 2 + t.integer :status, default: 0, null: false, limit: 2 + t.binary :fingerprint + t.bigint :issue_id, index: true + t.bigint :project_id, null: false + t.text :title, null: false + t.text :description + t.text :service + t.text :monitoring_tool + t.text :hosts, array: true, null: false, default: [] # rubocop:disable Migration/AddLimitToTextColumns + t.jsonb :payload, null: false, default: {} + + t.index %w(project_id iid), name: 'index_alert_management_alerts_on_project_id_and_iid', unique: true, using: :btree + t.index %w(project_id fingerprint), name: 'index_alert_management_alerts_on_project_id_and_fingerprint', unique: true, using: :btree + end + end + + add_text_limit :alert_management_alerts, :title, 200 + add_text_limit :alert_management_alerts, :description, 1000 + add_text_limit :alert_management_alerts, :service, 100 + add_text_limit :alert_management_alerts, :monitoring_tool, 100 + end + + def down + drop_table :alert_management_alerts + end +end diff --git a/db/migrate/20200421233150_add_foreign_keys_for_alert_management_alerts.rb b/db/migrate/20200421233150_add_foreign_keys_for_alert_management_alerts.rb new file mode 100644 index 00000000000..b16bdf9830c --- /dev/null +++ b/db/migrate/20200421233150_add_foreign_keys_for_alert_management_alerts.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class AddForeignKeysForAlertManagementAlerts < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_foreign_key :alert_management_alerts, :projects, column: :project_id, on_delete: :cascade + add_concurrent_foreign_key :alert_management_alerts, :issues, column: :issue_id, on_delete: :nullify + end + + def down + remove_foreign_key_if_exists :alert_management_alerts, column: :project_id + remove_foreign_key_if_exists :alert_management_alerts, column: :issue_id + end +end diff --git a/db/post_migrate/20200401091051_remove_reference_columns_from_resource_milestone_events.rb b/db/post_migrate/20200401091051_remove_reference_columns_from_resource_milestone_events.rb new file mode 100644 index 00000000000..639ab93cf18 --- /dev/null +++ b/db/post_migrate/20200401091051_remove_reference_columns_from_resource_milestone_events.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class RemoveReferenceColumnsFromResourceMilestoneEvents < ActiveRecord::Migration[6.0] + DOWNTIME = false + + def change + remove_column :resource_milestone_events, :reference, :text + remove_column :resource_milestone_events, :reference_html, :text + remove_column :resource_milestone_events, :cached_markdown_version, :integer + end +end diff --git a/db/structure.sql b/db/structure.sql index faf61a6986a..759465c6845 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -24,6 +24,40 @@ CREATE SEQUENCE public.abuse_reports_id_seq ALTER SEQUENCE public.abuse_reports_id_seq OWNED BY public.abuse_reports.id; +CREATE TABLE public.alert_management_alerts ( + id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + started_at timestamp with time zone NOT NULL, + ended_at timestamp with time zone, + events integer DEFAULT 1 NOT NULL, + iid integer NOT NULL, + severity smallint DEFAULT 0 NOT NULL, + status smallint DEFAULT 0 NOT NULL, + fingerprint bytea, + issue_id bigint, + project_id bigint NOT NULL, + title text NOT NULL, + description text, + service text, + monitoring_tool text, + hosts text[] DEFAULT '{}'::text[] NOT NULL, + payload jsonb DEFAULT '{}'::jsonb NOT NULL, + CONSTRAINT check_2df3e2fdc1 CHECK ((char_length(monitoring_tool) <= 100)), + CONSTRAINT check_5e9e57cadb CHECK ((char_length(description) <= 1000)), + CONSTRAINT check_bac14dddde CHECK ((char_length(service) <= 100)), + CONSTRAINT check_d1d1c2d14c CHECK ((char_length(title) <= 200)) +); + +CREATE SEQUENCE public.alert_management_alerts_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE public.alert_management_alerts_id_seq OWNED BY public.alert_management_alerts.id; + CREATE TABLE public.alerts_service_data ( id bigint NOT NULL, service_id integer NOT NULL, @@ -5597,9 +5631,6 @@ CREATE TABLE public.resource_milestone_events ( milestone_id bigint, action smallint NOT NULL, state smallint NOT NULL, - cached_markdown_version integer, - reference text, - reference_html text, created_at timestamp with time zone NOT NULL ); @@ -6661,10 +6692,11 @@ CREATE TABLE public.vulnerability_exports ( finished_at timestamp with time zone, status character varying(255) NOT NULL, file character varying(255), - project_id bigint NOT NULL, + project_id bigint, author_id bigint NOT NULL, file_store integer, - format smallint DEFAULT 0 NOT NULL + format smallint DEFAULT 0 NOT NULL, + group_id integer ); CREATE SEQUENCE public.vulnerability_exports_id_seq @@ -7014,6 +7046,8 @@ ALTER SEQUENCE public.zoom_meetings_id_seq OWNED BY public.zoom_meetings.id; ALTER TABLE ONLY public.abuse_reports ALTER COLUMN id SET DEFAULT nextval('public.abuse_reports_id_seq'::regclass); +ALTER TABLE ONLY public.alert_management_alerts ALTER COLUMN id SET DEFAULT nextval('public.alert_management_alerts_id_seq'::regclass); + ALTER TABLE ONLY public.alerts_service_data ALTER COLUMN id SET DEFAULT nextval('public.alerts_service_data_id_seq'::regclass); ALTER TABLE ONLY public.allowed_email_domains ALTER COLUMN id SET DEFAULT nextval('public.allowed_email_domains_id_seq'::regclass); @@ -7623,6 +7657,9 @@ ALTER TABLE ONLY public.zoom_meetings ALTER COLUMN id SET DEFAULT nextval('publi ALTER TABLE ONLY public.abuse_reports ADD CONSTRAINT abuse_reports_pkey PRIMARY KEY (id); +ALTER TABLE ONLY public.alert_management_alerts + ADD CONSTRAINT alert_management_alerts_pkey PRIMARY KEY (id); + ALTER TABLE ONLY public.alerts_service_data ADD CONSTRAINT alerts_service_data_pkey PRIMARY KEY (id); @@ -8686,6 +8723,12 @@ CREATE UNIQUE INDEX idx_vulnerability_issue_links_on_vulnerability_id_and_link_t CREATE INDEX index_abuse_reports_on_user_id ON public.abuse_reports USING btree (user_id); +CREATE INDEX index_alert_management_alerts_on_issue_id ON public.alert_management_alerts USING btree (issue_id); + +CREATE UNIQUE INDEX index_alert_management_alerts_on_project_id_and_fingerprint ON public.alert_management_alerts USING btree (project_id, fingerprint); + +CREATE UNIQUE INDEX index_alert_management_alerts_on_project_id_and_iid ON public.alert_management_alerts USING btree (project_id, iid); + CREATE INDEX index_alerts_service_data_on_service_id ON public.alerts_service_data USING btree (service_id); CREATE INDEX index_allowed_email_domains_on_group_id ON public.allowed_email_domains USING btree (group_id); @@ -10442,7 +10485,9 @@ CREATE INDEX index_vulnerabilities_on_updated_by_id ON public.vulnerabilities US CREATE INDEX index_vulnerability_exports_on_author_id ON public.vulnerability_exports USING btree (author_id); -CREATE UNIQUE INDEX index_vulnerability_exports_on_project_id_and_id ON public.vulnerability_exports USING btree (project_id, id); +CREATE INDEX index_vulnerability_exports_on_group_id_not_null ON public.vulnerability_exports USING btree (group_id) WHERE (group_id IS NOT NULL); + +CREATE INDEX index_vulnerability_exports_on_project_id_not_null ON public.vulnerability_exports USING btree (project_id) WHERE (project_id IS NOT NULL); CREATE INDEX index_vulnerability_feedback_on_author_id ON public.vulnerability_feedback USING btree (author_id); @@ -10639,6 +10684,9 @@ ALTER TABLE ONLY public.geo_container_repository_updated_events ALTER TABLE ONLY public.users_star_projects ADD CONSTRAINT fk_22cd27ddfc FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE; +ALTER TABLE ONLY public.alert_management_alerts + ADD CONSTRAINT fk_2358b75436 FOREIGN KEY (issue_id) REFERENCES public.issues(id) ON DELETE SET NULL; + ALTER TABLE ONLY public.ci_stages ADD CONSTRAINT fk_2360681d1d FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE; @@ -10894,6 +10942,9 @@ ALTER TABLE ONLY public.issues ALTER TABLE ONLY public.epics ADD CONSTRAINT fk_9d480c64b2 FOREIGN KEY (start_date_sourcing_epic_id) REFERENCES public.epics(id) ON DELETE SET NULL; +ALTER TABLE ONLY public.alert_management_alerts + ADD CONSTRAINT fk_9e49e5c2b7 FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE; + ALTER TABLE ONLY public.ci_pipeline_schedules ADD CONSTRAINT fk_9ea99f58d2 FOREIGN KEY (owner_id) REFERENCES public.users(id) ON DELETE SET NULL; @@ -10975,6 +11026,9 @@ ALTER TABLE ONLY public.design_management_versions ALTER TABLE ONLY public.geo_event_log ADD CONSTRAINT fk_c1f241c70d FOREIGN KEY (upload_deleted_event_id) REFERENCES public.geo_upload_deleted_events(id) ON DELETE CASCADE; +ALTER TABLE ONLY public.vulnerability_exports + ADD CONSTRAINT fk_c3d3cb5d0f FOREIGN KEY (group_id) REFERENCES public.namespaces(id) ON DELETE CASCADE; + ALTER TABLE ONLY public.geo_event_log ADD CONSTRAINT fk_c4b1c1f66e FOREIGN KEY (repository_deleted_event_id) REFERENCES public.geo_repository_deleted_events(id) ON DELETE CASCADE; @@ -13212,6 +13266,7 @@ COPY "schema_migrations" (version) FROM STDIN; 20200331132103 20200331195952 20200331220930 +20200401091051 20200401095430 20200401211005 20200402123926 @@ -13257,6 +13312,9 @@ COPY "schema_migrations" (version) FROM STDIN; 20200411125656 20200413072059 20200413230056 +20200414112444 +20200414114611 +20200414115801 20200414144547 20200415153154 20200415160722 @@ -13266,5 +13324,7 @@ COPY "schema_migrations" (version) FROM STDIN; 20200416111111 20200416120128 20200416120354 +20200417044453 +20200421233150 \. diff --git a/doc/administration/scaling/index.md b/doc/administration/scaling/index.md index f04517bc379..288dafbdc3d 100644 --- a/doc/administration/scaling/index.md +++ b/doc/administration/scaling/index.md @@ -58,6 +58,12 @@ On different cloud vendors a best effort like for like can be used. From 1 to 1,000 users, a single-node [Omnibus](https://docs.gitlab.com/omnibus/) setup with frequent backups is adequate. Please refer to the [installation documentation](../../install/README.md) and [backup/restore documentation](https://docs.gitlab.com/omnibus/settings/backups.html#backup-and-restore-omnibus-gitlab-configuration). +| Users | Configuration[^8] | GCP type | AWS type[^9] | +|-------|----------------------|---------------|--------------| +| 100 | 2 vCPU, 7.2GB Memory | n1-standard-2 | c5.2xlarge | +| 500 | 4 vCPU, 15GB Memory | n1-standard-4 | m5.xlarge | +| 1000 | 8 vCPU, 30GB Memory | n1-standard-8 | m5.2xlarge | + This solution is appropriate for many teams that have a single server at their disposal. With automatic backup of the GitLab repositories, configuration, and the database, this can be an optimal solution if you don't have strict availability requirements. You can also optionally configure GitLab to use an [external PostgreSQL service](../external_database.md) or an [external object storage service](../high_availability/object_storage.md) for added performance and reliability at a relatively low complexity cost. diff --git a/lib/banzai/filter/upload_link_filter.rb b/lib/banzai/filter/upload_link_filter.rb index 023c1288367..762371e1418 100644 --- a/lib/banzai/filter/upload_link_filter.rb +++ b/lib/banzai/filter/upload_link_filter.rb @@ -50,6 +50,10 @@ module Banzai Addressable::URI.join(Gitlab.config.gitlab.base_url, path).to_s end + if html_attr.name == 'href' + html_attr.parent.set_attribute('data-link', 'true') + end + html_attr.parent.add_class('gfm') end diff --git a/lib/gitlab/jira_import/issue_serializer.rb b/lib/gitlab/jira_import/issue_serializer.rb index cdcb62ac6e9..75056d87d99 100644 --- a/lib/gitlab/jira_import/issue_serializer.rb +++ b/lib/gitlab/jira_import/issue_serializer.rb @@ -4,14 +4,12 @@ module Gitlab module JiraImport class IssueSerializer attr_reader :jira_issue, :project, :params, :formatter - attr_accessor :metadata def initialize(project, jira_issue, params = {}) @jira_issue = jira_issue @project = project @params = params @formatter = Gitlab::ImportFormatter.new - @metadata = [] end def execute @@ -38,7 +36,7 @@ module Gitlab body << formatter.author_line(jira_issue.reporter.displayName) body << formatter.assignee_line(jira_issue.assignee.displayName) if jira_issue.assignee body << jira_issue.description - body << add_metadata + body << MetadataCollector.new(jira_issue).execute body.join end @@ -51,50 +49,6 @@ module Gitlab Issuable::STATE_ID_MAP[:opened] end end - - def add_metadata - add_field(%w(issuetype name), 'Issue type') - add_field(%w(priority name), 'Priority') - add_labels - add_field('environment', 'Environment') - add_field('duedate', 'Due date') - add_parent - add_versions - - return if metadata.empty? - - metadata.join("\n").prepend("\n\n---\n\n**Issue metadata**\n\n") - end - - def add_field(keys, field_label) - value = fields.dig(*keys) - return if value.blank? - - metadata << "- #{field_label}: #{value}" - end - - def add_labels - return if fields['labels'].blank? - - metadata << "- Labels: #{fields['labels'].join(', ')}" - end - - def add_parent - parent_issue_key = fields.dig('parent', 'key') - return if parent_issue_key.blank? - - metadata << "- Parent issue: [#{parent_issue_key}] #{fields['parent']['fields']['summary']}" - end - - def add_versions - return if fields['fixVersions'].blank? - - metadata << "- Fix versions: #{fields['fixVersions'].map { |version| version['name'] }.join(', ')}" - end - - def fields - jira_issue.fields - end end end end diff --git a/lib/gitlab/jira_import/metadata_collector.rb b/lib/gitlab/jira_import/metadata_collector.rb new file mode 100644 index 00000000000..49faef4cf49 --- /dev/null +++ b/lib/gitlab/jira_import/metadata_collector.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +module Gitlab + module JiraImport + class MetadataCollector + attr_accessor :jira_issue, :metadata + + def initialize(jira_issue) + @jira_issue = jira_issue + @metadata = [] + end + + def execute + add_field(%w(issuetype name), 'Issue type') + add_field(%w(priority name), 'Priority') + add_labels + add_field('environment', 'Environment') + add_field('duedate', 'Due date') + add_parent + add_versions + + return if metadata.empty? + + metadata.join("\n").prepend("\n\n---\n\n**Issue metadata**\n\n") + end + + private + + def add_field(keys, field_label) + value = fields.dig(*keys) + return if value.blank? + + metadata << "- #{field_label}: #{value}" + end + + def add_labels + return if fields['labels'].blank? + + metadata << "- Labels: #{fields['labels'].join(', ')}" + end + + def add_parent + parent_issue_key = fields.dig('parent', 'key') + return if parent_issue_key.blank? + + metadata << "- Parent issue: [#{parent_issue_key}] #{fields['parent']['fields']['summary']}" + end + + def add_versions + return if fields['fixVersions'].blank? + + metadata << "- Fix versions: #{fields['fixVersions'].map { |version| version['name'] }.join(', ')}" + end + + def fields + jira_issue.fields + end + end + end +end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 11e0b54f4eb..b235caaa6a2 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -2632,6 +2632,9 @@ msgstr "" msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules." msgstr "" +msgid "At least one logging option is required to be enabled" +msgstr "" + msgid "At least one of group_id or project_id must be specified" msgstr "" diff --git a/qa/Gemfile b/qa/Gemfile index 86d8aa5c025..95533a72e6c 100644 --- a/qa/Gemfile +++ b/qa/Gemfile @@ -1,7 +1,7 @@ source 'https://rubygems.org' gem 'gitlab-qa' -gem 'activesupport', '6.0.2' # This should stay in sync with the root's Gemfile +gem 'activesupport', '~> 6.0.2.2' # This should stay in sync with the root's Gemfile gem 'capybara', '~> 3.29.0' gem 'capybara-screenshot', '~> 1.0.23' gem 'rake', '~> 12.3.0' diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock index ececdab80d5..75531573a50 100644 --- a/qa/Gemfile.lock +++ b/qa/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - activesupport (6.0.2) + activesupport (6.0.2.2) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) @@ -114,7 +114,7 @@ PLATFORMS ruby DEPENDENCIES - activesupport (= 6.0.2) + activesupport (~> 6.0.2.2) airborne (~> 0.2.13) capybara (~> 3.29.0) capybara-screenshot (~> 1.0.23) diff --git a/qa/qa/page/project/issue/index.rb b/qa/qa/page/project/issue/index.rb index b5ad63ab8de..ace2537fc0e 100644 --- a/qa/qa/page/project/issue/index.rb +++ b/qa/qa/page/project/issue/index.rb @@ -9,6 +9,15 @@ module QA element :assignee_link end + view 'app/views/projects/issues/export_csv/_button.html.haml' do + element :export_as_csv_button + end + + view 'app/views/projects/issues/export_csv/_modal.html.haml' do + element :export_issues_button + element :export_issues_modal + end + view 'app/views/projects/issues/_issue.html.haml' do element :issue element :issue_link, 'link_to issue.title' # rubocop:disable QA/ElementWithPattern @@ -34,6 +43,18 @@ module QA click_element :closed_issues_link end + def click_export_as_csv_button + click_element(:export_as_csv_button) + end + + def click_export_issues_button + click_element(:export_issues_button) + end + + def export_issues_modal + find_element(:export_issues_modal) + end + def has_assignee_link_count?(count) all_elements(:assignee_link, count: count) end diff --git a/qa/qa/page/project/settings/ci_cd.rb b/qa/qa/page/project/settings/ci_cd.rb index b3cbf572cb7..c43974c10cb 100644 --- a/qa/qa/page/project/settings/ci_cd.rb +++ b/qa/qa/page/project/settings/ci_cd.rb @@ -13,20 +13,10 @@ module QA element :variables_settings_content end - view 'app/views/shared/deploy_tokens/_index.html.haml' do - element :deploy_tokens_settings - end - view 'app/views/projects/deploy_keys/_index.html.haml' do element :deploy_keys_settings end - def expand_deploy_tokens(&block) - expand_section(:deploy_tokens_settings) do - Settings::DeployTokens.perform(&block) - end - end - def expand_deploy_keys(&block) expand_section(:deploy_keys_settings) do Settings::DeployKeys.perform(&block) diff --git a/qa/qa/page/project/settings/repository.rb b/qa/qa/page/project/settings/repository.rb index 9b48e62a9f9..97dfc6497a4 100644 --- a/qa/qa/page/project/settings/repository.rb +++ b/qa/qa/page/project/settings/repository.rb @@ -15,6 +15,16 @@ module QA element :mirroring_repositories_settings_section end + view 'app/views/shared/deploy_tokens/_index.html.haml' do + element :deploy_tokens_settings + end + + def expand_deploy_tokens(&block) + expand_section(:deploy_tokens_settings) do + Settings::DeployTokens.perform(&block) + end + end + def expand_protected_branches(&block) expand_section(:protected_branches_settings) do ProtectedBranches.perform(&block) diff --git a/qa/qa/resource/deploy_token.rb b/qa/qa/resource/deploy_token.rb index b4baaa47349..0ba8dbbf287 100644 --- a/qa/qa/resource/deploy_token.rb +++ b/qa/qa/resource/deploy_token.rb @@ -6,16 +6,16 @@ module QA attr_accessor :name, :expires_at attribute :username do - Page::Project::Settings::CICD.perform do |cicd_page| - cicd_page.expand_deploy_tokens do |token| + Page::Project::Settings::Repository.perform do |repository_page| + repository_page.expand_deploy_tokens do |token| token.token_username end end end attribute :password do - Page::Project::Settings::CICD.perform do |cicd_page| - cicd_page.expand_deploy_tokens do |token| + Page::Project::Settings::Repository.perform do |repository_page| + repository_page.expand_deploy_tokens do |token| token.token_password end end @@ -31,10 +31,10 @@ module QA def fabricate! project.visit! - Page::Project::Menu.perform(&:go_to_ci_cd_settings) + Page::Project::Menu.perform(&:go_to_repository_settings) - Page::Project::Settings::CICD.perform do |cicd| - cicd.expand_deploy_tokens do |page| + Page::Project::Settings::Repository.perform do |setting| + setting.expand_deploy_tokens do |page| page.fill_token_name(name) page.fill_token_expires_at(expires_at) page.fill_scopes(read_repository: true, read_registry: false) diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb index 0facef85985..6891af54eb4 100644 --- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb +++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb @@ -266,84 +266,4 @@ describe Projects::Settings::CiCdController do end end end - - describe 'POST create_deploy_token' do - context 'when ajax_new_deploy_token feature flag is disabled for the project' do - before do - stub_feature_flags(ajax_new_deploy_token: { enabled: false, thing: project }) - end - - it_behaves_like 'a created deploy token' do - let(:entity) { project } - let(:create_entity_params) { { namespace_id: project.namespace, project_id: project } } - let(:deploy_token_type) { DeployToken.deploy_token_types[:project_type] } - end - end - - context 'when ajax_new_deploy_token feature flag is enabled for the project' do - let(:good_deploy_token_params) do - { - name: 'name', - expires_at: 1.day.from_now.to_s, - username: 'deployer', - read_repository: '1', - deploy_token_type: DeployToken.deploy_token_types[:project_type] - } - end - let(:request_params) do - { - namespace_id: project.namespace.to_param, - project_id: project.to_param, - deploy_token: deploy_token_params - } - end - - subject { post :create_deploy_token, params: request_params, format: :json } - - context('a good request') do - let(:deploy_token_params) { good_deploy_token_params } - let(:expected_response) do - { - 'id' => be_a(Integer), - 'name' => deploy_token_params[:name], - 'username' => deploy_token_params[:username], - 'expires_at' => Time.parse(deploy_token_params[:expires_at]), - 'token' => be_a(String), - 'scopes' => deploy_token_params.inject([]) do |scopes, kv| - key, value = kv - key.to_s.start_with?('read_') && !value.to_i.zero? ? scopes << key.to_s : scopes - end - } - end - - it 'creates the deploy token' do - subject - - expect(response).to have_gitlab_http_status(:created) - expect(response).to match_response_schema('public_api/v4/deploy_token') - expect(json_response).to match(expected_response) - end - end - - context('a bad request') do - let(:deploy_token_params) { good_deploy_token_params.except(:read_repository) } - let(:expected_response) { { 'message' => "Scopes can't be blank" } } - - it 'does not create the deploy token' do - subject - - expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response).to match(expected_response) - end - end - - context('an invalid request') do - let(:deploy_token_params) { good_deploy_token_params.except(:name) } - - it 'raises a validation error' do - expect { subject }.to raise_error(ActiveRecord::StatementInvalid) - end - end - end - end end diff --git a/spec/controllers/projects/settings/repository_controller_spec.rb b/spec/controllers/projects/settings/repository_controller_spec.rb index 67ae9ebda38..847c80e8917 100644 --- a/spec/controllers/projects/settings/repository_controller_spec.rb +++ b/spec/controllers/projects/settings/repository_controller_spec.rb @@ -32,4 +32,84 @@ describe Projects::Settings::RepositoryController do expect(RepositoryCleanupWorker).to have_received(:perform_async).once end end + + describe 'POST create_deploy_token' do + context 'when ajax_new_deploy_token feature flag is disabled for the project' do + before do + stub_feature_flags(ajax_new_deploy_token: { enabled: false, thing: project }) + end + + it_behaves_like 'a created deploy token' do + let(:entity) { project } + let(:create_entity_params) { { namespace_id: project.namespace, project_id: project } } + let(:deploy_token_type) { DeployToken.deploy_token_types[:project_type] } + end + end + + context 'when ajax_new_deploy_token feature flag is enabled for the project' do + let(:good_deploy_token_params) do + { + name: 'name', + expires_at: 1.day.from_now.to_s, + username: 'deployer', + read_repository: '1', + deploy_token_type: DeployToken.deploy_token_types[:project_type] + } + end + let(:request_params) do + { + namespace_id: project.namespace.to_param, + project_id: project.to_param, + deploy_token: deploy_token_params + } + end + + subject { post :create_deploy_token, params: request_params, format: :json } + + context('a good request') do + let(:deploy_token_params) { good_deploy_token_params } + let(:expected_response) do + { + 'id' => be_a(Integer), + 'name' => deploy_token_params[:name], + 'username' => deploy_token_params[:username], + 'expires_at' => Time.parse(deploy_token_params[:expires_at]), + 'token' => be_a(String), + 'scopes' => deploy_token_params.inject([]) do |scopes, kv| + key, value = kv + key.to_s.start_with?('read_') && !value.to_i.zero? ? scopes << key.to_s : scopes + end + } + end + + it 'creates the deploy token' do + subject + + expect(response).to have_gitlab_http_status(:created) + expect(response).to match_response_schema('public_api/v4/deploy_token') + expect(json_response).to match(expected_response) + end + end + + context('a bad request') do + let(:deploy_token_params) { good_deploy_token_params.except(:read_repository) } + let(:expected_response) { { 'message' => "Scopes can't be blank" } } + + it 'does not create the deploy token' do + subject + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response).to match(expected_response) + end + end + + context('an invalid request') do + let(:deploy_token_params) { good_deploy_token_params.except(:name) } + + it 'raises a validation error' do + expect { subject }.to raise_error(ActiveRecord::StatementInvalid) + end + end + end + end end diff --git a/spec/factories/alert_management/alerts.rb b/spec/factories/alert_management/alerts.rb new file mode 100644 index 00000000000..30eb1c0fc50 --- /dev/null +++ b/spec/factories/alert_management/alerts.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true +require 'ffaker' + +FactoryBot.define do + factory :alert_management_alert, class: 'AlertManagement::Alert' do + project + title { FFaker::Lorem.sentence } + started_at { Time.current } + + trait :with_issue do + issue + end + + trait :with_fingerprint do + fingerprint { SecureRandom.hex } + end + + trait :with_service do + service { FFaker::App.name } + end + + trait :with_monitoring_tool do + monitoring_tool { FFaker::App.name } + end + + trait :with_host do + hosts { FFaker::Internet.public_ip_v4_address } + end + + trait :resolved do + status { :resolved } + end + end +end diff --git a/spec/factories/clusters/applications/helm.rb b/spec/factories/clusters/applications/helm.rb index 728c83e01b4..0152b16c8ea 100644 --- a/spec/factories/clusters/applications/helm.rb +++ b/spec/factories/clusters/applications/helm.rb @@ -142,6 +142,8 @@ FactoryBot.define do factory :clusters_applications_fluentd, class: 'Clusters::Applications::Fluentd' do host { 'example.com' } + waf_log_enabled { true } + cilium_log_enabled { true } cluster factory: %i(cluster with_installed_helm provided_by_gcp) trait :no_helm_installed do diff --git a/spec/features/markdown/copy_as_gfm_spec.rb b/spec/features/markdown/copy_as_gfm_spec.rb index f85acc28645..d40c2b8bafd 100644 --- a/spec/features/markdown/copy_as_gfm_spec.rb +++ b/spec/features/markdown/copy_as_gfm_spec.rb @@ -172,6 +172,12 @@ describe 'Copy as GFM', :js do '![Image](https://example.com/image.png)' ) + verify_media_with_partial_path( + '[test.txt](/uploads/a123/image.txt)', + + project_media_uri(@project, '/uploads/a123/image.txt') + ) + verify_media_with_partial_path( '![Image](/uploads/a123/image.png)', diff --git a/spec/features/projects/settings/ci_cd_settings_spec.rb b/spec/features/projects/settings/ci_cd_settings_spec.rb index 75c890ec2e2..ed65dcd85ab 100644 --- a/spec/features/projects/settings/ci_cd_settings_spec.rb +++ b/spec/features/projects/settings/ci_cd_settings_spec.rb @@ -7,22 +7,6 @@ describe 'Projects > Settings > CI / CD settings' do let_it_be(:user) { create(:user) } let_it_be(:role) { :maintainer } - context 'Deploy tokens' do - let!(:deploy_token) { create(:deploy_token, projects: [project]) } - - before do - project.add_role(user, role) - sign_in(user) - stub_container_registry_config(enabled: true) - stub_feature_flags(ajax_new_deploy_token: { enabled: false, thing: project }) - visit project_settings_ci_cd_path(project) - end - - it_behaves_like 'a deploy token in settings' do - let(:entity_type) { 'project' } - end - end - context 'Deploy Keys', :js do let_it_be(:private_deploy_key) { create(:deploy_key, title: 'private_deploy_key', public: false) } let_it_be(:public_deploy_key) { create(:another_deploy_key, title: 'public_deploy_key', public: true) } diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb index 28a238a5423..0fd153f0518 100644 --- a/spec/features/projects/settings/repository_settings_spec.rb +++ b/spec/features/projects/settings/repository_settings_spec.rb @@ -25,6 +25,20 @@ describe 'Projects > Settings > Repository settings' do context 'for maintainer' do let(:role) { :maintainer } + context 'Deploy tokens' do + let!(:deploy_token) { create(:deploy_token, projects: [project]) } + + before do + stub_container_registry_config(enabled: true) + stub_feature_flags(ajax_new_deploy_token: { enabled: false, thing: project }) + visit project_settings_repository_path(project) + end + + it_behaves_like 'a deploy token in settings' do + let(:entity_type) { 'project' } + end + end + context 'remote mirror settings' do before do visit project_settings_repository_path(project) diff --git a/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb b/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb index 11b06604f27..a77240c5c33 100644 --- a/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb +++ b/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb @@ -12,7 +12,7 @@ describe 'Repository Settings > User sees revoke deploy token modal', :js do project.add_role(user, role) sign_in(user) stub_feature_flags(ajax_new_deploy_token: { enabled: false, thing: project }) - visit(project_settings_ci_cd_path(project)) + visit(project_settings_repository_path(project)) click_link('Revoke') end diff --git a/spec/fixtures/api/schemas/cluster_status.json b/spec/fixtures/api/schemas/cluster_status.json index ce62655648b..f6db336fe65 100644 --- a/spec/fixtures/api/schemas/cluster_status.json +++ b/spec/fixtures/api/schemas/cluster_status.json @@ -42,6 +42,8 @@ "host": {"type": ["string", "null"]}, "port": {"type": ["integer", "514"]}, "protocol": {"type": ["integer", "0"]}, + "waf_log_enabled": {"type": ["boolean", "true"]}, + "cilium_log_enabled": {"type": ["boolean", "true"]}, "update_available": { "type": ["boolean", "null"] }, "can_uninstall": { "type": "boolean" }, "available_domains": { diff --git a/spec/lib/banzai/filter/upload_link_filter_spec.rb b/spec/lib/banzai/filter/upload_link_filter_spec.rb index 3f181dce7bc..c366f774895 100644 --- a/spec/lib/banzai/filter/upload_link_filter_spec.rb +++ b/spec/lib/banzai/filter/upload_link_filter_spec.rb @@ -51,6 +51,7 @@ describe Banzai::Filter::UploadLinkFilter do expect(doc.at_css('a')['href']).to eq(absolute_path) expect(doc.at_css('a').classes).to include('gfm') + expect(doc.at_css('a')['data-link']).to eq('true') end end @@ -59,11 +60,13 @@ describe Banzai::Filter::UploadLinkFilter do expect(doc.at_css('a')['href']).to eq(relative_path) expect(doc.at_css('a').classes).to include('gfm') + expect(doc.at_css('a')['data-link']).to eq('true') doc = filter(nested(link(upload_path))) expect(doc.at_css('a')['href']).to eq(relative_path) expect(doc.at_css('a').classes).to include('gfm') + expect(doc.at_css('a')['data-link']).to eq('true') end it 'rebuilds relative URL for an image' do @@ -71,11 +74,13 @@ describe Banzai::Filter::UploadLinkFilter do expect(doc.at_css('img')['src']).to eq(relative_path) expect(doc.at_css('img').classes).to include('gfm') + expect(doc.at_css('img')['data-link']).not_to eq('true') doc = filter(nested(image(upload_path))) expect(doc.at_css('img')['src']).to eq(relative_path) expect(doc.at_css('img').classes).to include('gfm') + expect(doc.at_css('img')['data-link']).not_to eq('true') end it 'does not modify absolute URL' do @@ -83,6 +88,7 @@ describe Banzai::Filter::UploadLinkFilter do expect(doc.at_css('a')['href']).to eq 'http://example.com' expect(doc.at_css('a').classes).not_to include('gfm') + expect(doc.at_css('a')['data-link']).not_to eq('true') end it 'supports unescaped Unicode filenames' do @@ -91,6 +97,7 @@ describe Banzai::Filter::UploadLinkFilter do expect(doc.at_css('a')['href']).to eq("/#{project.full_path}/uploads/%ED%95%9C%EA%B8%80.png") expect(doc.at_css('a').classes).to include('gfm') + expect(doc.at_css('a')['data-link']).to eq('true') end it 'supports escaped Unicode filenames' do @@ -100,6 +107,7 @@ describe Banzai::Filter::UploadLinkFilter do expect(doc.at_css('img')['src']).to eq("/#{project.full_path}/uploads/%ED%95%9C%EA%B8%80.png") expect(doc.at_css('img').classes).to include('gfm') + expect(doc.at_css('img')['data-link']).not_to eq('true') end end @@ -118,6 +126,7 @@ describe Banzai::Filter::UploadLinkFilter do expect(doc.at_css('a')['href']).to eq(absolute_path) expect(doc.at_css('a').classes).to include('gfm') + expect(doc.at_css('a')['data-link']).to eq('true') end end @@ -126,6 +135,7 @@ describe Banzai::Filter::UploadLinkFilter do expect(doc.at_css('a')['href']).to eq(relative_path) expect(doc.at_css('a').classes).to include('gfm') + expect(doc.at_css('a')['data-link']).to eq('true') end it 'rewrites the link correctly for subgroup' do @@ -135,6 +145,7 @@ describe Banzai::Filter::UploadLinkFilter do expect(doc.at_css('a')['href']).to eq(relative_path) expect(doc.at_css('a').classes).to include('gfm') + expect(doc.at_css('a')['data-link']).to eq('true') end it 'does not modify absolute URL' do @@ -142,6 +153,7 @@ describe Banzai::Filter::UploadLinkFilter do expect(doc.at_css('a')['href']).to eq 'http://example.com' expect(doc.at_css('a').classes).not_to include('gfm') + expect(doc.at_css('a')['data-link']).not_to eq('true') end end @@ -159,6 +171,7 @@ describe Banzai::Filter::UploadLinkFilter do expect(doc.at_css('a')['href']).to eq(absolute_path) expect(doc.at_css('a').classes).to include('gfm') + expect(doc.at_css('a')['data-link']).to eq('true') end end @@ -178,6 +191,7 @@ describe Banzai::Filter::UploadLinkFilter do expect(doc.at_css('a')['href']).to eq(absolute_path) expect(doc.at_css('a').classes).to include('gfm') + expect(doc.at_css('a')['data-link']).to eq('true') end end @@ -186,6 +200,7 @@ describe Banzai::Filter::UploadLinkFilter do expect(doc.at_css('a')['href']).to eq(gitlab_root + relative_path) expect(doc.at_css('a').classes).to include('gfm') + expect(doc.at_css('a')['data-link']).to eq('true') end end @@ -194,6 +209,7 @@ describe Banzai::Filter::UploadLinkFilter do expect(doc.at_css('a')['href']).to eq(relative_path) expect(doc.at_css('a').classes).to include('gfm') + expect(doc.at_css('a')['data-link']).to eq('true') end it 'does not modify absolute URL' do @@ -201,6 +217,7 @@ describe Banzai::Filter::UploadLinkFilter do expect(doc.at_css('a')['href']).to eq 'http://example.com' expect(doc.at_css('a').classes).not_to include('gfm') + expect(doc.at_css('a')['data-link']).not_to eq('true') end end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index c3b0d4fa506..70d642fc477 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -39,6 +39,7 @@ issues: - related_vulnerabilities - user_mentions - system_note_metadata +- alert_management_alert events: - author - project @@ -481,6 +482,7 @@ project: - daily_report_results - jira_imports - compliance_framework_setting +- alert_management_alerts award_emoji: - awardable - user diff --git a/spec/lib/gitlab/jira_import/issue_serializer_spec.rb b/spec/lib/gitlab/jira_import/issue_serializer_spec.rb index 808ed6ee2fa..11dc784b821 100644 --- a/spec/lib/gitlab/jira_import/issue_serializer_spec.rb +++ b/spec/lib/gitlab/jira_import/issue_serializer_spec.rb @@ -18,22 +18,12 @@ describe Gitlab::JiraImport::IssueSerializer do let(:parent_field) do { 'key' => 'FOO-2', 'id' => '1050', 'fields' => { 'summary' => 'parent issue FOO' } } end - let(:issue_type_field) { { 'name' => 'Task' } } - let(:fix_versions_field) { [{ 'name' => '1.0' }, { 'name' => '1.1' }] } let(:priority_field) { { 'name' => 'Medium' } } - let(:labels_field) { %w(bug backend) } - let(:environment_field) { 'staging' } - let(:duedate_field) { '2020-03-01' } let(:fields) do { 'parent' => parent_field, - 'issuetype' => issue_type_field, - 'fixVersions' => fix_versions_field, - 'priority' => priority_field, - 'labels' => labels_field, - 'environment' => environment_field, - 'duedate' => duedate_field + 'priority' => priority_field } end @@ -68,13 +58,8 @@ describe Gitlab::JiraImport::IssueSerializer do **Issue metadata** - - Issue type: Task - Priority: Medium - - Labels: bug, backend - - Environment: staging - - Due date: 2020-03-01 - Parent issue: [FOO-2] parent issue FOO - - Fix versions: 1.0, 1.1 MD end @@ -91,54 +76,6 @@ describe Gitlab::JiraImport::IssueSerializer do author_id: project.creator_id ) end - - context 'when some metadata fields are missing' do - let(:assignee) { nil } - let(:parent_field) { nil } - let(:fix_versions_field) { [] } - let(:labels_field) { [] } - let(:environment_field) { nil } - let(:duedate_field) { '2020-03-01' } - - it 'skips the missing fields' do - expected_description = <<~MD - *Created by: Reporter* - - basic description - - --- - - **Issue metadata** - - - Issue type: Task - - Priority: Medium - - Due date: 2020-03-01 - MD - - expect(subject[:description]).to eq(expected_description.strip) - end - end - - context 'when all metadata fields are missing' do - let(:assignee) { nil } - let(:parent_field) { nil } - let(:issue_type_field) { nil } - let(:fix_versions_field) { [] } - let(:priority_field) { nil } - let(:labels_field) { [] } - let(:environment_field) { nil } - let(:duedate_field) { nil } - - it 'skips the whole metadata secction' do - expected_description = <<~MD - *Created by: Reporter* - - basic description - MD - - expect(subject[:description]).to eq(expected_description.strip) - end - end end context 'with done status' do diff --git a/spec/lib/gitlab/jira_import/metadata_collector_spec.rb b/spec/lib/gitlab/jira_import/metadata_collector_spec.rb new file mode 100644 index 00000000000..77c38c658ea --- /dev/null +++ b/spec/lib/gitlab/jira_import/metadata_collector_spec.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::JiraImport::MetadataCollector do + describe '#execute' do + let(:key) { 'PROJECT-5' } + let(:summary) { 'some title' } + let(:description) { 'basic description' } + let(:created_at) { '2020-01-01 20:00:00' } + let(:updated_at) { '2020-01-10 20:00:00' } + let(:assignee) { double(displayName: 'Solver') } + let(:jira_status) { 'new' } + + let(:parent_field) do + { 'key' => 'FOO-2', 'id' => '1050', 'fields' => { 'summary' => 'parent issue FOO' } } + end + let(:issue_type_field) { { 'name' => 'Task' } } + let(:fix_versions_field) { [{ 'name' => '1.0' }, { 'name' => '1.1' }] } + let(:priority_field) { { 'name' => 'Medium' } } + let(:labels_field) { %w(bug backend) } + let(:environment_field) { 'staging' } + let(:duedate_field) { '2020-03-01' } + + let(:fields) do + { + 'parent' => parent_field, + 'issuetype' => issue_type_field, + 'fixVersions' => fix_versions_field, + 'priority' => priority_field, + 'labels' => labels_field, + 'environment' => environment_field, + 'duedate' => duedate_field + } + end + let(:jira_issue) do + double( + id: '1234', + key: key, + summary: summary, + description: description, + created: created_at, + updated: updated_at, + assignee: assignee, + reporter: double(displayName: 'Reporter'), + status: double(statusCategory: { 'key' => jira_status }), + fields: fields + ) + end + + subject { described_class.new(jira_issue).execute } + + context 'when all metadata fields are present' do + it 'skips writes all fields' do + expected_result = <<~MD + --- + + **Issue metadata** + + - Issue type: Task + - Priority: Medium + - Labels: bug, backend + - Environment: staging + - Due date: 2020-03-01 + - Parent issue: [FOO-2] parent issue FOO + - Fix versions: 1.0, 1.1 + MD + + expect(subject.strip).to eq(expected_result.strip) + end + end + + context 'when some metadata fields are missing' do + let(:assignee) { nil } + let(:parent_field) { nil } + let(:fix_versions_field) { [] } + let(:labels_field) { [] } + let(:environment_field) { nil } + + it 'skips the missing fields' do + expected_result = <<~MD + --- + + **Issue metadata** + + - Issue type: Task + - Priority: Medium + - Due date: 2020-03-01 + MD + + expect(subject.strip).to eq(expected_result.strip) + end + end + + context 'when all metadata fields are missing' do + let(:assignee) { nil } + let(:parent_field) { nil } + let(:issue_type_field) { nil } + let(:fix_versions_field) { [] } + let(:priority_field) { nil } + let(:labels_field) { [] } + let(:environment_field) { nil } + let(:duedate_field) { nil } + + it 'returns nil' do + expect(subject).to be_nil + end + end + end +end diff --git a/spec/models/alert_management/alert_spec.rb b/spec/models/alert_management/alert_spec.rb new file mode 100644 index 00000000000..be9c076efd1 --- /dev/null +++ b/spec/models/alert_management/alert_spec.rb @@ -0,0 +1,119 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe AlertManagement::Alert do + describe 'associations' do + it { is_expected.to belong_to(:project) } + it { is_expected.to belong_to(:issue) } + end + + describe 'validations' do + it { is_expected.to validate_presence_of(:title) } + it { is_expected.to validate_presence_of(:events) } + it { is_expected.to validate_presence_of(:severity) } + it { is_expected.to validate_presence_of(:status) } + it { is_expected.to validate_presence_of(:started_at) } + + it { is_expected.to validate_length_of(:title).is_at_most(200) } + it { is_expected.to validate_length_of(:description).is_at_most(1000) } + it { is_expected.to validate_length_of(:service).is_at_most(100) } + it { is_expected.to validate_length_of(:monitoring_tool).is_at_most(100) } + + describe 'fingerprint' do + let_it_be(:fingerprint) { 'fingerprint' } + let_it_be(:existing_alert) { create(:alert_management_alert, fingerprint: fingerprint) } + let(:new_alert) { build(:alert_management_alert, fingerprint: fingerprint, project: project) } + + subject { new_alert } + + context 'adding an alert with the same fingerprint' do + context 'same project' do + let(:project) { existing_alert.project } + + it { is_expected.not_to be_valid } + end + + context 'different project' do + let(:project) { create(:project) } + + it { is_expected.to be_valid } + end + end + end + + describe 'hosts' do + subject(:alert) { build(:alert_management_alert, hosts: hosts) } + + context 'over 255 total chars' do + let(:hosts) { ['111.111.111.111'] * 18 } + + it { is_expected.not_to be_valid } + end + + context 'under 255 chars' do + let(:hosts) { ['111.111.111.111'] * 17 } + + it { is_expected.to be_valid } + end + end + end + + describe 'enums' do + let(:severity_values) do + { critical: 0, high: 1, medium: 2, low: 3, info: 4, unknown: 5 } + end + + let(:status_values) do + { triggered: 0, acknowledged: 1, resolved: 2, ignored: 3 } + end + + it { is_expected.to define_enum_for(:severity).with_values(severity_values) } + it { is_expected.to define_enum_for(:status).with_values(status_values) } + end + + describe 'fingerprint setter' do + let(:alert) { build(:alert_management_alert) } + + subject(:set_fingerprint) { alert.fingerprint = fingerprint } + + let(:fingerprint) { 'test' } + + it 'sets to the SHA1 of the value' do + expect { set_fingerprint } + .to change { alert.fingerprint } + .from(nil) + .to(Digest::SHA1.hexdigest(fingerprint)) + end + + describe 'testing length of 40' do + where(:input) do + [ + 'test', + 'another test', + 'a' * 1000, + 12345 + ] + end + + with_them do + let(:fingerprint) { input } + + it 'sets the fingerprint to 40 chars' do + set_fingerprint + expect(alert.fingerprint.size).to eq(40) + end + end + end + + context 'blank value given' do + let(:fingerprint) { '' } + + it 'does not set the fingerprint' do + expect { set_fingerprint } + .not_to change { alert.fingerprint } + .from(nil) + end + end + end +end diff --git a/spec/models/clusters/applications/fluentd_spec.rb b/spec/models/clusters/applications/fluentd_spec.rb index 7e9680b0ab4..4e9548990ed 100644 --- a/spec/models/clusters/applications/fluentd_spec.rb +++ b/spec/models/clusters/applications/fluentd_spec.rb @@ -3,7 +3,9 @@ require 'spec_helper' describe Clusters::Applications::Fluentd do - let(:fluentd) { create(:clusters_applications_fluentd) } + let(:waf_log_enabled) { true } + let(:cilium_log_enabled) { true } + let(:fluentd) { create(:clusters_applications_fluentd, waf_log_enabled: waf_log_enabled, cilium_log_enabled: cilium_log_enabled) } include_examples 'cluster application core specs', :clusters_applications_fluentd include_examples 'cluster application status specs', :clusters_applications_fluentd @@ -47,4 +49,36 @@ describe Clusters::Applications::Fluentd do expect(values).to include('output.conf', 'general.conf') end end + + describe '#values' do + let(:modsecurity_log_path) { "/var/log/containers/*#{Clusters::Applications::Ingress::MODSECURITY_LOG_CONTAINER_NAME}*.log" } + let(:cilium_log_path) { "/var/log/containers/*#{described_class::CILIUM_CONTAINER_NAME}*.log" } + + subject { fluentd.values } + + context 'with both logs variables set to false' do + let(:waf_log_enabled) { false } + let(:cilium_log_enabled) { false } + + it "raises ActiveRecord::RecordInvalid" do + expect {subject}.to raise_error(ActiveRecord::RecordInvalid) + end + end + + context 'with both logs variables set to true' do + it { is_expected.to include("#{modsecurity_log_path},#{cilium_log_path}") } + end + + context 'with waf_log_enabled set to true' do + let(:cilium_log_enabled) { false } + + it { is_expected.to include(modsecurity_log_path) } + end + + context 'with cilium_log_enabled set to true' do + let(:waf_log_enabled) { false } + + it { is_expected.to include(cilium_log_path) } + end + end end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index e8103be0682..0ac64e3cc42 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -14,6 +14,7 @@ describe Issue do it { is_expected.to have_many(:assignees) } it { is_expected.to have_many(:user_mentions).class_name("IssueUserMention") } it { is_expected.to have_one(:sentry_issue) } + it { is_expected.to have_one(:alert_management_alert) } end describe 'modules' do diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index ddcc9123118..d7811ff839d 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -110,6 +110,7 @@ describe Project do it { is_expected.to have_many(:source_pipelines) } it { is_expected.to have_many(:prometheus_alert_events) } it { is_expected.to have_many(:self_managed_prometheus_alert_events) } + it { is_expected.to have_many(:alert_management_alerts) } it { is_expected.to have_many(:jira_imports) } it_behaves_like 'model with repository' do diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index c1b1150c28b..fd6cccba959 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -800,9 +800,8 @@ describe 'project routing' do it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/settings/repository", "/gitlab/gitlabhq/-/settings/repository" - # TODO: remove this test as part of https://gitlab.com/gitlab-org/gitlab/issues/207079 (12.9) - it 'to ci_cd#create_deploy_token' do - expect(post('gitlab/gitlabhq/-/settings/ci_cd/deploy_token/create')).to route_to('projects/settings/ci_cd#create_deploy_token', namespace_id: 'gitlab', project_id: 'gitlabhq') + it 'to repository#create_deploy_token' do + expect(post('gitlab/gitlabhq/-/settings/ci_cd/deploy_token/create')).to route_to('projects/settings/repository#create_deploy_token', namespace_id: 'gitlab', project_id: 'gitlabhq') end end diff --git a/spec/serializers/cluster_application_entity_spec.rb b/spec/serializers/cluster_application_entity_spec.rb index 873fbf812cc..b81bdaa0d72 100644 --- a/spec/serializers/cluster_application_entity_spec.rb +++ b/spec/serializers/cluster_application_entity_spec.rb @@ -77,5 +77,17 @@ describe ClusterApplicationEntity do expect(subject[:pages_domain]).to eq(id: pages_domain.id, domain: pages_domain.domain) end end + + context 'for fluentd application' do + let(:application) { build(:clusters_applications_fluentd, :installed) } + + it 'includes host, port, protocol and log fields' do + expect(subject[:port]).to eq(514) + expect(subject[:host]).to eq("example.com") + expect(subject[:protocol]).to eq("tcp") + expect(subject[:waf_log_enabled]).to be true + expect(subject[:cilium_log_enabled]).to be true + end + end end end