Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-04-23 00:09:41 +00:00
parent 22e60f1c61
commit b158645575
57 changed files with 917 additions and 306 deletions

View file

@ -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();
});

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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')

View file

@ -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

View file

@ -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
<buffer kubernetes_host>
</buffer>
<format>
@ -85,7 +94,7 @@ module Clusters
<source>
@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
</source>
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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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) }

View file

@ -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'

View file

@ -0,0 +1,5 @@
---
title: Add table for Alert Management alerts
merge_request: 29864
author:
type: added

View file

@ -0,0 +1,5 @@
---
title: Fixed copy as GFM not copying upload links
merge_request: 29683
author:
type: fixed

View file

@ -0,0 +1,5 @@
---
title: Remove obsolete columns from resource_milestone_events
merge_request: 28536
author:
type: other

View file

@ -0,0 +1,5 @@
---
title: Update Fluentd model to support multiple logs
merge_request: 29458
author:
type: changed

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
\.

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 ""

View file

@ -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'

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)',

View file

@ -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) }

View file

@ -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)

View file

@ -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

View file

@ -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": {

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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