Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-12-13 12:09:26 +00:00
parent 2d8c28f1d3
commit 9fdb3dbd6b
91 changed files with 1315 additions and 159 deletions

View File

@ -169,6 +169,7 @@ Naming/FileName:
- 'qa/tasks/**/*.rake'
- '**/*.ru'
- 'app/graphql/types/issue_connection.rb'
- 'app/graphql/types/group_connection.rb'
IgnoreExecutableScripts: true
AllowedAcronyms:

View File

@ -1 +1 @@
c2dd35e7775018bd29c2b1306afeee966ba3b4d7
cf32d208912de9dfbfdd4baab42655baf82bfce5

View File

@ -353,7 +353,7 @@ gem 'snowplow-tracker', '~> 0.6.1'
# Metrics
gem 'webrick', '~> 1.6.1', require: false
gem 'prometheus-client-mmap', '~> 0.16', require: 'prometheus/client'
gem 'prometheus-client-mmap', '~> 0.17', require: 'prometheus/client'
gem 'warning', '~> 1.3.0'

View File

@ -428,7 +428,7 @@
{"name":"premailer","version":"1.16.0","platform":"ruby","checksum":"03e4402c448e6bae13fb5f6301a8bde4f3508e1bff90ae7c0972c7be94694786"},
{"name":"premailer-rails","version":"1.10.3","platform":"ruby","checksum":"7cdcb97027866f7a81c490c6d15ada7f39666b5f6375f0821b7e97e0483b112f"},
{"name":"proc_to_ast","version":"0.1.0","platform":"ruby","checksum":"92a73fa66e2250a83f8589f818b0751bcf227c68f85916202df7af85082f8691"},
{"name":"prometheus-client-mmap","version":"0.16.2","platform":"ruby","checksum":"36e7e96fdd603c2d1fed050ec71504797f3f8b2560123306ba72018ee3561165"},
{"name":"prometheus-client-mmap","version":"0.17.0","platform":"ruby","checksum":"766d3706f7b26fed5a177843ab15b5b0dc108f9677d8bdbe0c4b5d9375c2af24"},
{"name":"pry","version":"0.13.1","platform":"java","checksum":"9612d825e2c3bc160633b2a4fae21041126ee33f1ac8035c851417e561b2b46c"},
{"name":"pry","version":"0.13.1","platform":"ruby","checksum":"1393918c415af46b6d09044d2b78dde92b29bc834fd85c369a950bab0826dc47"},
{"name":"pry-byebug","version":"3.9.0","platform":"ruby","checksum":"3bba08f97fea15b89cc299f3b5136e3b85763cd18cf84960eac4fbfbeb2ede24"},

View File

@ -1082,7 +1082,7 @@ GEM
coderay
parser
unparser
prometheus-client-mmap (0.16.2)
prometheus-client-mmap (0.17.0)
pry (0.13.1)
coderay (~> 1.1)
method_source (~> 1.0)
@ -1768,7 +1768,7 @@ DEPENDENCIES
pg_query (~> 2.2)
png_quantizator (~> 0.2.1)
premailer-rails (~> 1.10.3)
prometheus-client-mmap (~> 0.16)
prometheus-client-mmap (~> 0.17)
pry-byebug
pry-rails (~> 0.3.9)
pry-shell (~> 0.5.1)

View File

@ -607,53 +607,65 @@
"secrets": {
"type": "object",
"markdownDescription": "Defines secrets to be injected as environment variables. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#secrets).",
"additionalProperties": {
"type": "object",
"description": "Environment variable name",
"properties": {
"vault": {
"oneOf": [
{
"type": "string",
"description": "The secret to be fetched from Vault (e.g. 'production/db/password@ops' translates to secret 'ops/data/production/db', field `password`)"
},
{
"type": "object",
"properties": {
"engine": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"path": {
"type": "string"
}
},
"required": [
"name",
"path"
]
},
"path": {
"type": "string"
},
"field": {
"type": "string"
}
"patternProperties": {
".*": {
"type": "object",
"properties": {
"vault": {
"oneOf": [
{
"type": "string",
"markdownDescription": "The secret to be fetched from Vault (e.g. 'production/db/password@ops' translates to secret 'ops/data/production/db', field `password`). [Learn More](https://docs.gitlab.com/ee/ci/yaml/#secretsvault)"
},
"required": [
"engine",
"path",
"field"
]
}
]
}
},
"required": [
"vault"
]
{
"type": "object",
"properties": {
"engine": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"path": {
"type": "string"
}
},
"required": [
"name",
"path"
]
},
"path": {
"type": "string"
},
"field": {
"type": "string"
}
},
"required": [
"engine",
"path",
"field"
],
"additionalProperties": false
}
]
},
"file": {
"type": "boolean",
"default": true,
"markdownDescription": "Configures the secret to be stored as either a file or variable type CI/CD variable. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#secretsfile)"
},
"token": {
"type": "string",
"description": "Specifies the JWT variable that should be used to authenticate with Hashicorp Vault."
}
},
"required": [
"vault"
],
"additionalProperties": false
}
}
},
"before_script": {

View File

@ -20,7 +20,7 @@ module Mutations
alert = authorized_find!(project_path: args[:project_path], iid: args[:iid])
result = set_assignees(alert, args[:assignee_usernames], args[:operation_mode])
track_usage_event(:incident_management_alert_assigned, current_user.id)
track_alert_events('incident_management_alert_assigned', alert)
prepare_response(result)
end

View File

@ -11,7 +11,7 @@ module Mutations
alert = authorized_find!(project_path: args[:project_path], iid: args[:iid])
result = ::AlertManagement::Alerts::Todo::CreateService.new(alert, current_user).execute
track_usage_event(:incident_management_alert_todo, current_user.id)
track_alert_events('incident_management_alert_todo', alert)
prepare_response(result)
end

View File

@ -39,6 +39,24 @@ module Mutations
::AlertManagement::AlertsFinder.new(current_user, project, args).execute.first
end
def track_alert_events(event, alert)
project = alert.project
namespace = project.namespace
track_usage_event(event, current_user.id)
return unless Feature.enabled?(:route_hll_to_snowplow_phase2, namespace)
Gitlab::Tracking.event(
self.class.to_s,
event,
project: project,
namespace: namespace,
user: current_user,
label: 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly',
context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event).to_context]
)
end
end
end
end

View File

@ -9,7 +9,7 @@ module Mutations
alert = authorized_find!(project_path: args[:project_path], iid: args[:iid])
result = create_alert_issue(alert, current_user)
track_usage_event(:incident_management_incident_created, current_user.id)
track_alert_events('incident_management_incident_created', alert)
track_usage_event(:incident_management_alert_create_incident, current_user.id)
prepare_response(alert, result)

View File

@ -13,7 +13,7 @@ module Mutations
alert = authorized_find!(project_path: project_path, iid: iid)
result = update_status(alert, status)
track_usage_event(:incident_management_alert_status_changed, current_user.id)
track_alert_events('incident_management_alert_status_changed', alert)
prepare_response(result)
end

View File

@ -38,8 +38,8 @@ module Types
field :executor_name, GraphQL::Types::String, null: true,
description: 'Executor last advertised by the runner.',
method: :executor_name
field :groups, ::Types::GroupType.connection_type, null: true,
description: 'Groups the runner is associated with. For group runners only.'
field :groups, 'Types::GroupConnection', null: true,
description: 'Groups the runner is associated with. For group runners only.'
field :id, ::Types::GlobalIDType[::Ci::Runner], null: false,
description: 'ID of the runner.'
field :ip_address, GraphQL::Types::String, null: true,

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
# Normally this wouldn't be needed and we could use
#
# type Types::GroupType.connection_type, null: true
#
# in a resolver. However we can end up with cyclic definitions.
# Running the spec locally can result in errors like
#
# NameError: uninitialized constant Types::GroupType
#
# or other errors. To fix this, we created this file and use
#
# type "Types::GroupConnection", null: true
#
# which gives a delayed resolution, and the proper connection type.
#
# See gitlab/app/graphql/types/ci/runner_type.rb
# Reference: https://github.com/rmosolgo/graphql-ruby/issues/3974#issuecomment-1084444214
# and https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#testing-tips-and-tricks
#
Types::GroupConnection = Types::GroupType.connection_type

View File

@ -22,7 +22,7 @@ module Groups
# When running Observability UI in standalone mode (i.e. not backed by Observability Backend)
# the group-id is not required. This is mostly used for local dev
base_url = ENV['STANDALONE_OBSERVABILITY_UI'] == 'true' ? observability_url : "#{observability_url}/#{group.id}"
base_url = ENV['STANDALONE_OBSERVABILITY_UI'] == 'true' ? observability_url : "#{observability_url}/-/#{group.id}"
sanitized_path = if params[:observability_path] && sanitize(params[:observability_path]) != ''
CGI.unescapeHTML(sanitize(params[:observability_path]))

View File

@ -23,7 +23,8 @@ module Users
namespace_storage_limit_banner_alert_threshold: 12, # EE-only
namespace_storage_limit_banner_error_threshold: 13, # EE-only
usage_quota_trial_alert: 14, # EE-only
preview_usage_quota_free_plan_alert: 15 # EE-only
preview_usage_quota_free_plan_alert: 15, # EE-only
enforcement_at_limit_alert: 16 # EE-only
}
validates :group, presence: true

View File

@ -7,7 +7,23 @@ module IncidentManagement
def track_incident_action(current_user, target, action)
return unless target.incident?
track_usage_event(:"incident_management_#{action}", current_user.id)
event = "incident_management_#{action}"
track_usage_event(event, current_user.id)
namespace = target.try(:namespace)
project = target.try(:project)
return unless Feature.enabled?(:route_hll_to_snowplow_phase2, target.try(:namespace))
Gitlab::Tracking.event(
self.class.to_s,
event,
project: project,
namespace: namespace,
user: current_user,
label: 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly',
context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event).to_context]
)
end
end
end

View File

@ -24,6 +24,23 @@ module IncidentManagement
def error_in_save(timeline_event)
error(timeline_event.errors.full_messages.to_sentence)
end
def track_timeline_event(event, project)
namespace = project.namespace
track_usage_event(event, user.id)
return unless Feature.enabled?(:route_hll_to_snowplow_phase2, namespace)
Gitlab::Tracking.event(
self.class.to_s,
event,
project: project,
namespace: namespace,
user: user,
label: 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly',
context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event).to_context]
)
end
end
end
end

View File

@ -106,7 +106,7 @@ module IncidentManagement
create_timeline_event_tag_links(timeline_event, params[:timeline_event_tag_names])
track_usage_event(:incident_management_timeline_event_created, user.id)
track_timeline_event("incident_management_timeline_event_created", project)
success(timeline_event)
else

View File

@ -18,7 +18,7 @@ module IncidentManagement
if timeline_event.destroy
add_system_note(incident, user)
track_usage_event(:incident_management_timeline_event_deleted, user.id)
track_timeline_event('incident_management_timeline_event_deleted', project)
success(timeline_event)
else
error_in_save(timeline_event)

View File

@ -43,7 +43,7 @@ module IncidentManagement
if timeline_event_saved
add_system_note(timeline_event)
track_usage_event(:incident_management_timeline_event_edited, user.id)
track_timeline_event('incident_management_timeline_event_edited', timeline_event.project)
success(timeline_event)
else
error_in_save(timeline_event)

View File

@ -114,6 +114,11 @@ module Issues
Milestones::IssuesCountService.new(milestone).delete_cache
end
override :allowed_create_params
def allowed_create_params(params)
super(params).except(:issue_type, :work_item_type_id, :work_item_type)
end
end
end

View File

@ -146,7 +146,7 @@ module Issues
# don't enqueue immediately to prevent todos removal in case of a mistake
TodosDestroyer::ConfidentialIssueWorker.perform_in(Todo::WAIT_FOR_DELETE, issue.id) if issue.confidential?
create_confidentiality_note(issue)
track_usage_event(:incident_management_incident_change_confidential, current_user.id)
track_incident_action(current_user, issue, :incident_change_confidential)
end
end

View File

@ -166,8 +166,9 @@ class TodoService
# When user marks a target as todo
def mark_todo(target, current_user)
attributes = attributes_for_todo(target.project, target, current_user, Todo::MARKED)
create_todos(current_user, attributes)
project = target.project
attributes = attributes_for_todo(project, target, current_user, Todo::MARKED)
create_todos(current_user, attributes, project&.namespace, project)
end
def todo_exist?(issuable, current_user)
@ -214,8 +215,9 @@ class TodoService
end
def create_request_review_todo(target, author, reviewers)
attributes = attributes_for_todo(target.project, target, author, Todo::REVIEW_REQUESTED)
create_todos(reviewers, attributes)
project = target.project
attributes = attributes_for_todo(project, target, author, Todo::REVIEW_REQUESTED)
create_todos(reviewers, attributes, project.namespace, project)
end
def create_member_access_request(member)
@ -225,12 +227,20 @@ class TodoService
approvers = source.access_request_approvers_to_be_notified.map(&:user)
return true if approvers.empty?
create_todos(approvers, attributes)
if source.instance_of? Project
project = source
namespace = project.namespace
else
project = nil
namespace = source
end
create_todos(approvers, attributes, namespace, project)
end
private
def create_todos(users, attributes)
def create_todos(users, attributes, namespace, project)
users = Array(users)
return if users.empty?
@ -256,7 +266,7 @@ class TodoService
todos = users.map do |user|
issue_type = attributes.delete(:issue_type)
track_todo_creation(user, issue_type)
track_todo_creation(user, issue_type, namespace, project)
Todo.create(attributes.merge(user_id: user.id))
end
@ -296,9 +306,10 @@ class TodoService
def create_assignment_todo(target, author, old_assignees = [])
if target.assignees.any?
project = target.project
assignees = target.assignees - old_assignees
attributes = attributes_for_todo(target.project, target, author, Todo::ASSIGNED)
create_todos(assignees, attributes)
attributes = attributes_for_todo(project, target, author, Todo::ASSIGNED)
create_todos(assignees, attributes, project.namespace, project)
end
end
@ -313,22 +324,24 @@ class TodoService
# Create Todos for directly addressed users
directly_addressed_users = filter_directly_addressed_users(parent, note || target, author, skip_users)
attributes = attributes_for_todo(parent, target, author, Todo::DIRECTLY_ADDRESSED, note)
create_todos(directly_addressed_users, attributes)
create_todos(directly_addressed_users, attributes, parent&.namespace, parent)
# Create Todos for mentioned users
mentioned_users = filter_mentioned_users(parent, note || target, author, skip_users + directly_addressed_users)
attributes = attributes_for_todo(parent, target, author, Todo::MENTIONED, note)
create_todos(mentioned_users, attributes)
create_todos(mentioned_users, attributes, parent&.namespace, parent)
end
def create_build_failed_todo(merge_request, todo_author)
attributes = attributes_for_todo(merge_request.project, merge_request, todo_author, Todo::BUILD_FAILED)
create_todos(todo_author, attributes)
project = merge_request.project
attributes = attributes_for_todo(project, merge_request, todo_author, Todo::BUILD_FAILED)
create_todos(todo_author, attributes, project.namespace, project)
end
def create_unmergeable_todo(merge_request, todo_author)
attributes = attributes_for_todo(merge_request.project, merge_request, todo_author, Todo::UNMERGEABLE)
create_todos(todo_author, attributes)
project = merge_request.project
attributes = attributes_for_todo(project, merge_request, todo_author, Todo::UNMERGEABLE)
create_todos(todo_author, attributes, project.namespace, project)
end
def attributes_for_target(target)
@ -392,10 +405,23 @@ class TodoService
PendingTodosFinder.new(users, criteria).execute
end
def track_todo_creation(user, issue_type)
def track_todo_creation(user, issue_type, namespace, project)
return unless issue_type == 'incident'
track_usage_event(:incident_management_incident_todo, user.id)
event = "incident_management_incident_todo"
track_usage_event(event, user.id)
return unless Feature.enabled?(:route_hll_to_snowplow_phase2, namespace)
Gitlab::Tracking.event(
self.class.to_s,
event,
project: project,
namespace: namespace,
user: user,
label: 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly',
context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event).to_context]
)
end
def attributes_for_access_request_todos(source, author, action, note = nil)

View File

@ -1659,6 +1659,15 @@
:weight: 1
:idempotent: true
:tags: []
- :name: package_repositories:packages_debian_process_package_file
:worker_name: Packages::Debian::ProcessPackageFileWorker
:feature_category: :package_registry
:has_external_dependencies: false
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent: true
:tags: []
- :name: package_repositories:packages_go_sync_packages
:worker_name: Packages::Go::SyncPackagesWorker
:feature_category: :package_registry

View File

@ -0,0 +1,52 @@
# frozen_string_literal: true
module Packages
module Debian
class ProcessPackageFileWorker
include ApplicationWorker
include ::Packages::FIPS
include Gitlab::Utils::StrongMemoize
data_consistency :always
deduplicate :until_executed
idempotent!
queue_namespace :package_repositories
feature_category :package_registry
def perform(package_file_id, user_id, distribution_name, component_name)
raise DisabledError, 'Debian registry is not FIPS compliant' if Gitlab::FIPS.enabled?
@package_file_id = package_file_id
@user_id = user_id
@distribution_name = distribution_name
@component_name = component_name
return unless package_file && user && distribution_name && component_name
# return if file has already been processed
return unless package_file.debian_file_metadatum&.unknown?
::Packages::Debian::ProcessPackageFileService.new(package_file, user, distribution_name, component_name).execute
rescue StandardError => e
raise if e.instance_of?(DisabledError)
Gitlab::ErrorTracking.log_exception(e, package_file_id: @package_file_id, user_id: @user_id,
distribution_name: @distribution_name, component_name: @component_name)
package_file.destroy!
end
private
def package_file
::Packages::PackageFile.find_by_id(@package_file_id)
end
strong_memoize_attr :package_file
def user
::User.find_by_id(@user_id)
end
strong_memoize_attr :user
end
end
end

View File

@ -0,0 +1,24 @@
---
description: Mirrored Service Ping Redis metric. Count of unique users that published incidents per month
category: StatusPage::PublishService
action: incident_management_incident_published
label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
property_description:
value_description:
extra_properties:
identifiers:
- project
- user
- namespace
product_section: ops
product_stage: monitor
product_group: monitor
product_category:
milestone: "15.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
distributions:
- ee
tiers:
- premium
- ultimate

View File

@ -0,0 +1,26 @@
---
description: Migrated Service Ping metric. Count of unique users adding alerts to the TODO list
category: Mutations::AlertManagement::Alerts::Todo::Create
action: incident_management_alert_todo
label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
property_description:
value_description:
extra_properties:
identifiers:
- project
- user
- namespace
product_section: ops
product_stage: monitor
product_group: monitor
product_category:
milestone: "15.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223/diffs
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -0,0 +1,26 @@
---
description: Count of unique users assigning an alert per week. Migrated form Service Ping metric
category: Mutations::AlertManagement::Alerts::SetAssignees
action: incident_management_alert_assigned
label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
property_description:
value_description:
extra_properties:
identifiers:
- project
- user
- namespace
product_section: ops
product_stage: monitor
product_group: monitor
product_category:
milestone: "15.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -0,0 +1,26 @@
---
description: Migrated from Service Ping metric. Count of unique users creating incidents
category: Mutations::AlertManagement::CreateAlertIssue
action: incident_management_incident_created
label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
property_description:
value_description:
extra_properties:
identifiers:
- project
- user
- namespace
product_section: ops
product_stage: monitor
product_group: monitor
product_category:
milestone: "15.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -0,0 +1,26 @@
---
description: Count of unique users changing alert's status. Migrated from Service Ping metric
category: Mutations::AlertManagement::UpdateAlertStatus
action: incident_management_alert_status_changed
label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
property_description:
value_description:
extra_properties:
identifiers:
- project
- user
- namespace
product_section: ops
product_stage: monitor
product_group: monitor
product_category:
milestone: "15.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -0,0 +1,27 @@
---
description: Count of unique users created timeline events
category: IncidentManagement::TimelineEvents::CreateService
action: incident_management_timeline_event_created
label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
property_description:
value_description:
extra_properties:
identifiers:
- project
- user
- namespace
product_section: ops
product_stage: monitor
product_group: respond
product_category: incident_management
value_type: number
milestone: "15.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -0,0 +1,26 @@
---
category: IncidentManagement::TimelineEvents::DestroyService
action: incident_management_timeline_event_deleted
label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
property_description:
value_description:
extra_properties:
identifiers:
- project
- user
- namespace
description: "Event migrates from Service Ping metric. Count of unique users deleted timeline events"
product_section: ops
product_stage: monitor
product_group: respond
product_category: incident_management
milestone: "15.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -0,0 +1,26 @@
---
category: IncidentManagement::TimelineEvents::UpdateService
action: incident_management_timeline_event_edited
label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
property_description:
value_description:
extra_properties:
identifiers:
- project
- user
- namespace
description: "Event migrated form Service Ping metric. Count of unique users edited timeline events"
product_section: ops
product_stage: monitor
product_group: respond
product_category: incident_management
milestone: "15.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -0,0 +1,26 @@
---
category: IssueLinks::CreateService
action: incident_management_incident_relate
label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
property_description:
value_description:
extra_properties:
identifiers:
- project
- user
- namespace
description: "Count of unique users adding issues per that are related to an incident. Migrated from Service Ping"
product_section: ops
product_stage: monitor
product_group: monitor
product_category:
milestone: "15.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -0,0 +1,26 @@
---
category: IssueLinks::DestroyService
action: incident_management_incident_unrelate
label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
property_description:
value_description:
extra_properties:
identifiers:
- project
- user
- namespace
milestone: "15.7"
description: "Count of unique users removing issue that are related to an incident. Migrated from Service Ping metric"
product_section: ops
product_stage: monitor
product_group: monitor
product_category:
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -0,0 +1,26 @@
---
category: Issues::CloseService
action: incident_management_incident_closed
label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
property_description:
value_description:
extra_properties:
identifiers:
- project
- user
- namespace
description: "Count of users closing incidents. Migrated from Service Ping metric."
product_section: ops
product_stage: monitor
product_group: monitor
product_category:
milestone: "15.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -0,0 +1,26 @@
---
category: Issues::ReopenService
action: incident_management_incident_reopened
label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
property_description:
value_description:
extra_properties:
identifiers:
- project
- user
- namespace
description: "Count of unique users reopening incidents. Migrated from Service Ping metric."
product_section: ops
product_stage: monitor
product_group: monitor
product_category:
milestone: "15.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -0,0 +1,26 @@
---
category: Issues::UpdateService
action: incident_management_incident_change_confidential
label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
property_description:
value_description:
extra_properties:
identifiers:
- project
- user
- namespace
description: "Count of unique users changing incidents to confidential. Event migrated from Service Ping metric."
product_section: ops
product_stage: monitor
product_group: monitor
product_category:
milestone: "15.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -0,0 +1,26 @@
---
category: Issues::ZoomLinkService
action: incident_management_incident_zoom_meeting
label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
property_description:
value_description:
extra_properties:
identifiers:
- project
- user
- namespace
description: "Count of unique users creating Zoom meetings about incidents. Event migrated from Service Ping metric."
product_section: ops
product_stage: monitor
product_group: monitor
product_category:
milestone: "15.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -0,0 +1,26 @@
---
category: Notes::CreateService
action: incident_management_incident_comment
label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
property_description:
value_description:
extra_properties:
identifiers:
- project
- user
- namespace
description: "Count of unique users adding comments on incidents. Event migrated from Service Ping metric"
product_section: ops
product_stage: monitor
product_group: monitor
product_category:
milestone: "15.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -0,0 +1,26 @@
---
category: TodoService
action: incident_management_incident_todo
label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
property_description:
value_description:
extra_properties:
identifiers:
- project
- user
- namespace
description: "Count of unique users adding incidents to the TODO list. Event migrated from Service Ping metric"
product_section: ops
product_stage: monitor
product_group: monitor
product_category:
milestone: "15.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -0,0 +1,26 @@
---
description: Count of unique users assiging incidents per
category: Issues::UpdateService
action: incident_management_incident_assigned
label_description: "Mirrored Service Ping total metric key_path: redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly"
property_description:
value_description:
extra_properties:
identifiers:
- project
- user
- namespace
product_section: ops
product_stage: monitor
product_group: monitor
product_category:
milestone: "15.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105223
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -6,9 +6,11 @@ product_stage: create
product_group: code_review
product_category: code_review
value_type: number
status: active
status: removed
milestone: "15.6"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103334
removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106449
milestone_removed: "15.7"
time_frame: 28d
data_source: database
data_category: optional

View File

@ -18772,6 +18772,7 @@ Represents a requirement.
| <a id="requirementtitlehtml"></a>`titleHtml` | [`String`](#string) | GitLab Flavored Markdown rendering of `title`. |
| <a id="requirementupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp of when the requirement was last updated. |
| <a id="requirementuserpermissions"></a>`userPermissions` | [`RequirementPermissions!`](#requirementpermissions) | Permissions for the current user on the resource. |
| <a id="requirementworkitemiid"></a>`workItemIid` | [`ID!`](#id) | Work item IID of the requirement, will replace current IID as identifier soon. |
#### Fields with arguments

View File

@ -26,12 +26,6 @@ Contributions are welcome.
For an introduction and basic steps, see
[How to make GitLab API calls](https://www.youtube.com/watch?v=0LsMC3ZiXkA).
## SCIM API **(PREMIUM SAAS)**
GitLab provides a [SCIM API](scim.md) that both implements
[the RFC7644 protocol](https://www.rfc-editor.org/rfc/rfc7644) and provides the
`/Users` endpoint. The base URL is `/api/scim/v2/groups/:group_path/Users/`.
## GraphQL API
A GraphQL API is available in GitLab.

View File

@ -8,6 +8,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98354) in GitLab 15.5.
GitLab provides an SCIM API that both implements [the RFC7644 protocol](https://tools.ietf.org/html/rfc7644)
and provides the `/Users` endpoint. The base URL is `/api/scim/v2/groups/:group_path/Users/`.
To use this API, [Group SSO](../user/group/saml_sso/index.md) must be enabled for the group.
This API is only in use where [SCIM for Group SSO](../user/group/saml_sso/scim_setup.md) is enabled. It's a prerequisite to the creation of SCIM identities.

View File

@ -176,3 +176,56 @@ Parameters:
| ---------- | -------------- | -------- | --------------------------------------------------------------------------------------------------------------- |
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
| `tag_name` | string | yes | The name of a tag |
## Get X.509 signature of a tag
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106578) in GitLab 15.7.
Get the [X.509 signature from a tag](../user/project/repository/x509_signed_commits/index.md#sign-commits-and-tags-with-x509-certificates),
if it is signed. Unsigned tags return a `404 Not Found` response.
```plaintext
GET /projects/:id/repository/tags/:tag_name/signature
```
Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
| `tag_name` | string | yes | The name of a tag. |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/repository/tags/v1.1.1/signature"
```
Example response if tag is X.509 signed:
```json
{
"signature_type": "X509",
"verification_status": "unverified",
"x509_certificate": {
"id": 1,
"subject": "CN=gitlab@example.org,OU=Example,O=World",
"subject_key_identifier": "BC:BC:BC:BC:BC:BC:BC:BC:BC:BC:BC:BC:BC:BC:BC:BC:BC:BC:BC:BC",
"email": "gitlab@example.org",
"serial_number": 278969561018901340486471282831158785578,
"certificate_status": "good",
"x509_issuer": {
"id": 1,
"subject": "CN=PKI,OU=Example,O=World",
"subject_key_identifier": "AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB:AB",
"crl_url": "http://example.com/pki.crl"
}
}
}
```
Example response if tag is unsigned:
```json
{
"message": "404 GPG Signature Not Found"
}
```

View File

@ -1,6 +1,6 @@
---
status: proposed
creation-date: 2022-11-09
creation-date: "2022-11-09"
authors: [ "@ankitbhatnagar" ]
coach: "@mappelman"
approvers: [ "@sebastienpahl", "@nicholasklick" ]

View File

@ -228,7 +228,10 @@ projects in a group, allowing tighter control over project membership.
For example, if you want to lock the group for an [Audit Event](../../administration/audit_events.md),
you can guarantee that project membership cannot be modified during the audit.
You can still invite groups or to add members to groups, implicitly giving members access to projects in the **locked** group.
If group membership lock is enabled, the group owner can still:
- Invite groups or add members to groups to give them access to projects in the **locked** group.
- Change the role of group members.
The setting does not cascade. Projects in subgroups observe the subgroup configuration, ignoring the parent group.
@ -239,8 +242,10 @@ To prevent members from being added to projects in a group:
1. Under **Membership**, select **Users cannot be added to projects in this group**.
1. Select **Save changes**.
All users who previously had permissions can no longer add members to a group.
API requests to add a new user to a project are not possible.
After you lock the membership for a group:
- All users who previously had permissions can no longer add members to a group.
- API requests to add a new user to a project are not possible.
## Manage group memberships via LDAP **(PREMIUM SELF)**

View File

@ -1,6 +1,6 @@
---
stage: Create
group: Source Code
stage: Plan
group: Project Management
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

View File

@ -53,12 +53,12 @@ Global search only flags with an error any search that includes more than:
To start a search, type your search query in the search bar on the top-right of the screen.
You must type at least two characters.
![basic search](img/basic_search_v15_1.png)
![search navbar](img/search_navbar_v15_7.png)
After the results are displayed, you can modify the search, select a different type of data to
search, or choose a specific group or project.
![basic search results](img/basic_search_results_v15_1.png)
![search scope](img/search_scope_v15_7.png)
## Search in code

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
module API
module Entities
class TagSignature < Grape::Entity
expose :signature_type, documentation: { type: 'string', example: 'PGP' }
expose :signature, merge: true do |tag|
::API::Entities::X509Signature.represent tag.signature if tag.signature_type == :X509
end
end
end
end

View File

@ -129,6 +129,24 @@ module API
end
end
end
desc "Get a tag's signature" do
success code: 200, model: Entities::TagSignature
tags %w[tags]
failure [
{ code: 404, message: 'Not found' }
]
end
params do
requires :tag_name, type: String, desc: 'The name of the tag'
end
get ':id/repository/tags/:tag_name/signature', requirements: TAG_ENDPOINT_REQUIREMENTS, feature_category: :source_code_management do
tag = user_project.repository.find_tag(params[:tag_name])
not_found! 'Tag' unless tag
not_found! 'Signature' unless tag.has_signature?
present tag, with: Entities::TagSignature
end
end
end
end

View File

@ -1,15 +0,0 @@
# frozen_string_literal: true
module Gitlab
module Usage
module Metrics
module Instrumentations
class CountMergeRequestAuthorsMetric < DatabaseMetric
operation :distinct_count, column: :author_id
relation { MergeRequest }
end
end
end
end
end

View File

@ -43034,6 +43034,11 @@ msgstr ""
msgid "To import an SVN repository, check out %{svn_link}."
msgstr ""
msgid "To invite more users, you can reduce the number of users in your namespace to %{free_limit} user or less. You can also upgrade to a paid tier which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgid_plural "To invite more users, you can reduce the number of users in your namespace to %{free_limit} users or less. You can also upgrade to a paid tier which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
msgstr[0] ""
msgstr[1] ""
msgid "To keep this project going, create a new issue"
msgstr ""
@ -47997,6 +48002,9 @@ msgstr ""
msgid "Your name"
msgstr ""
msgid "Your namespace %{namespace_name} has reached the %{free_limit} user limit"
msgstr ""
msgid "Your namespace %{namespace_name} is over the %{free_limit} user limit and has been placed in a read-only state."
msgstr ""

View File

@ -373,7 +373,7 @@ function display_deployment_debug() {
local namespace="${CI_ENVIRONMENT_SLUG}"
# Install dig to inspect DNS entries
apt update && apt install dnsutils
apt update && apt install -y dnsutils
echoinfo "[debugging data] Check review-app webservice DNS entry:"
dig +short $(echo "${CI_ENVIRONMENT_URL}" | sed 's~http[s]*://~~g')

View File

@ -13,7 +13,7 @@ FactoryBot.define do
# Expect base_types to exist on the DB
if type_base_attributes.slice(:namespace, :namespace_id).compact.empty?
WorkItems::Type.find_or_initialize_by(type_base_attributes).tap { |type| type.assign_attributes(attributes) }
WorkItems::Type.find_or_initialize_by(type_base_attributes)
else
WorkItems::Type.new(attributes)
end

View File

@ -32,6 +32,7 @@ import VariablesYaml from './yaml_tests/positive_tests/variables.yml';
import JobWhenYaml from './yaml_tests/positive_tests/job_when.yml';
import IdTokensYaml from './yaml_tests/positive_tests/id_tokens.yml';
import HooksYaml from './yaml_tests/positive_tests/hooks.yml';
import SecretsYaml from './yaml_tests/positive_tests/secrets.yml';
// YAML NEGATIVE TEST
import ArtifactsNegativeYaml from './yaml_tests/negative_tests/artifacts.yml';
@ -50,6 +51,7 @@ import VariablesInvalidSyntaxDescYaml from './yaml_tests/negative_tests/variable
import VariablesWrongSyntaxUsageExpand from './yaml_tests/negative_tests/variables/wrong_syntax_usage_expand.yml';
import IdTokensNegativeYaml from './yaml_tests/negative_tests/id_tokens.yml';
import HooksNegative from './yaml_tests/negative_tests/hooks.yml';
import SecretsNegativeYaml from './yaml_tests/negative_tests/secrets.yml';
const ajv = new Ajv({
strictTypes: false,
@ -87,6 +89,7 @@ describe('positive tests', () => {
VariablesYaml,
ProjectPathYaml,
IdTokensYaml,
SecretsYaml,
}),
)('schema validates %s', (_, input) => {
// We construct a new "JSON" from each main key that is inside a
@ -122,6 +125,7 @@ describe('negative tests', () => {
ProjectPathIncludeLeadSlashYaml,
ProjectPathIncludeNoSlashYaml,
ProjectPathIncludeTailSlashYaml,
SecretsNegativeYaml,
TriggerNegative,
HooksNegative,
}),

View File

@ -0,0 +1,39 @@
job_with_secrets_without_vault:
script:
- echo $TEST_DB_PASSWORD
secrets:
TEST_DB_PASSWORD:
token: $TEST_TOKEN
job_with_secrets_with_extra_properties:
script:
- echo $TEST_DB_PASSWORD
secrets:
TEST_DB_PASSWORD:
vault: test/db/password
extra_prop: TEST
job_with_secrets_with_invalid_vault_property:
script:
- echo $TEST_DB_PASSWORD
secrets:
TEST_DB_PASSWORD:
vault:
invalid: TEST
job_with_secrets_with_missing_required_vault_property:
script:
- echo $TEST_DB_PASSWORD
secrets:
TEST_DB_PASSWORD:
vault:
path: gitlab
job_with_secrets_with_missing_required_engine_property:
script:
- echo $TEST_DB_PASSWORD
secrets:
TEST_DB_PASSWORD:
vault:
engine:
path: kv

View File

@ -0,0 +1,28 @@
valid_job_with_secrets:
script:
- echo $TEST_DB_PASSWORD
secrets:
TEST_DB_PASSWORD:
vault: test/db/password
valid_job_with_secrets_and_token:
script:
- echo $TEST_DB_PASSWORD
secrets:
TEST_DB_PASSWORD:
vault: test/db/password
token: $TEST_TOKEN
valid_job_with_secrets_with_every_vault_keyword:
script:
- echo $TEST_DB_PASSWORD
secrets:
TEST_DB_PASSWORD:
vault:
engine:
name: test-engine
path: test
path: test/db
field: password
file: true
token: $TEST_TOKEN

View File

@ -56,6 +56,15 @@ RSpec.describe Mutations::AlertManagement::Alerts::SetAssignees do
context 'when operation mode is not specified' do
it_behaves_like 'successful resolution'
it_behaves_like 'an incident management tracked event', :incident_management_alert_assigned
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { project.namespace.reload }
let(:category) { described_class.to_s }
let(:user) { current_user }
let(:action) { 'incident_management_alert_assigned' }
let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
end
end
context 'when user does not have permission to update alerts' do

View File

@ -19,6 +19,15 @@ RSpec.describe Mutations::AlertManagement::Alerts::Todo::Create do
it_behaves_like 'an incident management tracked event', :incident_management_alert_todo
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { project.namespace.reload }
let(:category) { described_class.to_s }
let(:user) { current_user }
let(:action) { 'incident_management_alert_todo' }
let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
end
context 'when user does not have permissions' do
let(:current_user) { nil }

View File

@ -30,6 +30,15 @@ RSpec.describe Mutations::AlertManagement::CreateAlertIssue do
it_behaves_like 'an incident management tracked event', :incident_management_incident_created
it_behaves_like 'an incident management tracked event', :incident_management_alert_create_incident
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { project.namespace.reload }
let(:category) { described_class.to_s }
let(:user) { current_user }
let(:action) { 'incident_management_incident_created' }
let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
end
end
context 'when CreateAlertIssue responds with an error' do
@ -46,6 +55,15 @@ RSpec.describe Mutations::AlertManagement::CreateAlertIssue do
errors: ['An issue already exists']
)
end
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { project.namespace.reload }
let(:category) { described_class.to_s }
let(:user) { current_user }
let(:action) { 'incident_management_incident_created' }
let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
end
end
end

View File

@ -35,6 +35,15 @@ RSpec.describe Mutations::AlertManagement::UpdateAlertStatus do
let(:user) { current_user }
end
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { project.namespace }
let(:category) { described_class.to_s }
let(:user) { current_user }
let(:action) { 'incident_management_alert_status_changed' }
let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
end
context 'error occurs when updating' do
it 'returns the alert with errors' do
# Stub an error on the alert

View File

@ -10,17 +10,17 @@ RSpec.describe Groups::ObservabilityHelper do
context 'if observability_path is missing from params' do
it 'returns the iframe src for action: dashboards' do
allow(helper).to receive(:params).and_return({ action: 'dashboards' })
expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/#{group.id}/")
expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/-/#{group.id}/")
end
it 'returns the iframe src for action: manage' do
allow(helper).to receive(:params).and_return({ action: 'manage' })
expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/#{group.id}/dashboards")
expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/-/#{group.id}/dashboards")
end
it 'returns the iframe src for action: explore' do
allow(helper).to receive(:params).and_return({ action: 'explore' })
expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/#{group.id}/explore")
expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/-/#{group.id}/explore")
end
end
@ -28,7 +28,7 @@ RSpec.describe Groups::ObservabilityHelper do
context 'if observability_path is valid' do
it 'returns the iframe src by injecting the observability path' do
allow(helper).to receive(:params).and_return({ action: '/explore', observability_path: '/foo?bar=foobar' })
expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/#{group.id}/foo?bar=foobar")
expect(helper.observability_iframe_src(group)).to eq("#{observability_url}/-/#{group.id}/foo?bar=foobar")
end
end
@ -40,7 +40,7 @@ RSpec.describe Groups::ObservabilityHelper do
"/test?groupId=<script>alert('attack!')</script>"
})
expect(helper.observability_iframe_src(group)).to eq(
"#{observability_url}/#{group.id}/test?groupId=alert('attack!')"
"#{observability_url}/-/#{group.id}/test?groupId=alert('attack!')"
)
end
end

View File

@ -162,6 +162,13 @@ RSpec.describe Feature, stub_feature_flags: false do
stub_feature_flag_definition(:enabled_feature_flag, default_enabled: true)
end
context 'when using redis cache', :use_clean_rails_redis_caching do
it 'does not make recursive feature-flag calls' do
expect(described_class).to receive(:enabled?).once.and_call_original
described_class.enabled?(:disabled_feature_flag)
end
end
context 'when self-recursive' do
before do
allow(Feature).to receive(:with_feature).and_wrap_original do |original, name, &block|

View File

@ -1,25 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountMergeRequestAuthorsMetric do
let(:expected_value) { 1 }
let(:start) { 30.days.ago.to_s(:db) }
let(:finish) { 2.days.ago.to_s(:db) }
let(:expected_query) do
"SELECT COUNT(DISTINCT \"merge_requests\".\"author_id\") FROM \"merge_requests\"" \
" WHERE \"merge_requests\".\"created_at\" BETWEEN '#{start}' AND '#{finish}'"
end
before do
user = create(:user)
user2 = create(:user)
create(:merge_request, created_at: 1.year.ago, author: user)
create(:merge_request, created_at: 1.week.ago, author: user2)
create(:merge_request, created_at: 1.week.ago, author: user2)
end
it_behaves_like 'a correct instrumented metric value and query', { time_frame: '28d' }
end

View File

@ -632,7 +632,7 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
end
context 'when unsupported widget input is sent' do
let_it_be(:test_case) { create(:work_item_type, :default, :test_case, name: 'some_test_case_name') }
let_it_be(:test_case) { create(:work_item_type, :default, :test_case) }
let_it_be(:work_item) { create(:work_item, work_item_type: test_case, project: project) }
let(:input) do
@ -642,7 +642,7 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
end
it_behaves_like 'a mutation that returns top-level errors',
errors: ["Following widget keys are not supported by some_test_case_name type: [:hierarchy_widget]"]
errors: ["Following widget keys are not supported by Test Case type: [:hierarchy_widget]"]
end
end
end

View File

@ -479,4 +479,60 @@ RSpec.describe API::Tags, feature_category: :source_code_management do
end
end
end
describe 'GET /projects/:id/repository/tags/:tag_name/signature' do
let_it_be(:project) { create(:project, :repository, :public) }
let(:project_id) { project.id }
let(:route) { "/projects/#{project_id}/repository/tags/#{tag_name}/signature" }
context 'when tag does not exist' do
let(:tag_name) { 'unknown' }
it_behaves_like '404 response' do
let(:request) { get api(route, current_user) }
let(:message) { '404 Tag Not Found' }
end
end
context 'unsigned tag' do
let(:tag_name) { 'v1.1.0' }
it_behaves_like '404 response' do
let(:request) { get api(route, current_user) }
let(:message) { '404 Signature Not Found' }
end
end
context 'x509 signed tag' do
let(:tag_name) { 'v1.1.1' }
let(:tag) { project.repository.find_tag(tag_name) }
let(:signature) { tag.signature }
let(:x509_certificate) { signature.x509_certificate }
let(:x509_issuer) { x509_certificate.x509_issuer }
it 'returns correct JSON' do
get api(route, current_user)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq(
'signature_type' => 'X509',
'verification_status' => signature.verification_status.to_s,
'x509_certificate' => {
'id' => x509_certificate.id,
'subject' => x509_certificate.subject,
'subject_key_identifier' => x509_certificate.subject_key_identifier,
'email' => x509_certificate.email,
'serial_number' => x509_certificate.serial_number,
'certificate_status' => x509_certificate.certificate_status,
'x509_issuer' => {
'id' => x509_issuer.id,
'subject' => x509_issuer.subject,
'subject_key_identifier' => x509_issuer.subject_key_identifier,
'crl_url' => x509_issuer.crl_url
}
}
)
end
end
end
end

View File

@ -73,21 +73,21 @@ RSpec.describe Groups::ObservabilityController, feature_category: :tracing do
describe 'GET #dashboards' do
let(:path) { group_observability_dashboards_path(group) }
let(:expected_observability_path) { "#{observability_url}/#{group.id}/" }
let(:expected_observability_path) { "#{observability_url}/-/#{group.id}/" }
it_behaves_like 'observability route request'
end
describe 'GET #manage' do
let(:path) { group_observability_manage_path(group) }
let(:expected_observability_path) { "#{observability_url}/#{group.id}/dashboards" }
let(:expected_observability_path) { "#{observability_url}/-/#{group.id}/dashboards" }
it_behaves_like 'observability route request'
end
describe 'GET #explore' do
let(:path) { group_observability_explore_path(group) }
let(:expected_observability_path) { "#{observability_url}/#{group.id}/explore" }
let(:expected_observability_path) { "#{observability_url}/-/#{group.id}/explore" }
it_behaves_like 'observability route request'
end

View File

@ -55,6 +55,15 @@ RSpec.describe IncidentManagement::TimelineEvents::CreateService do
end
it_behaves_like 'an incident management tracked event', :incident_management_timeline_event_created
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { project.namespace.reload }
let(:category) { described_class.to_s }
let(:user) { current_user }
let(:action) { 'incident_management_timeline_event_created' }
let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
end
end
subject(:execute) { service.execute }
@ -276,6 +285,15 @@ RSpec.describe IncidentManagement::TimelineEvents::CreateService do
it_behaves_like 'an incident management tracked event', :incident_management_timeline_event_created
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { project.namespace.reload }
let(:category) { described_class.to_s }
let(:user) { current_user }
let(:action) { 'incident_management_timeline_event_created' }
let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
end
it 'successfully creates a database record', :aggregate_failures do
expect { execute }.to change { ::IncidentManagement::TimelineEvent.count }.by(1)
end

View File

@ -65,6 +65,15 @@ RSpec.describe IncidentManagement::TimelineEvents::DestroyService do
end
it_behaves_like 'an incident management tracked event', :incident_management_timeline_event_deleted
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { project.namespace.reload }
let(:category) { described_class.to_s }
let(:user) { current_user }
let(:action) { 'incident_management_timeline_event_deleted' }
let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
end
end
end
end

View File

@ -48,6 +48,14 @@ RSpec.describe IncidentManagement::TimelineEvents::UpdateService do
end
it_behaves_like 'an incident management tracked event', :incident_management_timeline_event_edited
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { project.namespace.reload }
let(:category) { described_class.to_s }
let(:action) { 'incident_management_timeline_event_edited' }
let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
end
end
shared_examples 'error response' do |message|

View File

@ -41,6 +41,14 @@ RSpec.describe IssueLinks::CreateService do
it_behaves_like 'an incident management tracked event', :incident_management_incident_relate do
let(:current_user) { user }
end
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { issue.namespace }
let(:category) { described_class.to_s }
let(:action) { 'incident_management_incident_relate' }
let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
end
end
end
end

View File

@ -25,6 +25,14 @@ RSpec.describe IssueLinks::DestroyService do
it_behaves_like 'an incident management tracked event', :incident_management_incident_unrelate do
let(:current_user) { user }
end
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { issue_b.namespace }
let(:category) { described_class.to_s }
let(:action) { 'incident_management_incident_unrelate' }
let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
end
end
end
end

View File

@ -99,6 +99,14 @@ RSpec.describe Issues::CloseService do
it_behaves_like 'an incident management tracked event', :incident_management_incident_closed
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { issue.namespace }
let(:category) { described_class.to_s }
let(:action) { 'incident_management_incident_closed' }
let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
end
it 'creates a new escalation resolved escalation status', :aggregate_failures do
expect { service.execute(issue) }.to change { IncidentManagement::IssuableEscalationStatus.where(issue: issue).count }.by(1)

View File

@ -9,21 +9,22 @@ RSpec.describe Issues::CreateService do
let_it_be_with_reload(:project) { create(:project, :public, group: group) }
let_it_be(:user) { create(:user) }
let(:opts) { { title: 'title' } }
let(:spam_params) { double }
let(:service) { described_class.new(project: project, current_user: user, params: opts, spam_params: spam_params) }
it_behaves_like 'rate limited service' do
let(:key) { :issues_create }
let(:key_scope) { %i[project current_user external_author] }
let(:application_limit_key) { :issues_create_limit }
let(:created_model) { Issue }
let(:service) { described_class.new(project: project, current_user: user, params: { title: 'title' }, spam_params: double) }
end
describe '#execute' do
let_it_be(:assignee) { create(:user) }
let_it_be(:milestone) { create(:milestone, project: project) }
let(:result) { described_class.new(project: project, current_user: user, params: opts, spam_params: spam_params).execute }
let(:result) { service.execute }
let(:issue) { result[:issue] }
before do
@ -54,6 +55,7 @@ RSpec.describe Issues::CreateService do
let(:opts) do
{ title: 'Awesome issue',
issue_type: :task,
description: 'please fix',
assignee_ids: [assignee.id],
label_ids: labels.map(&:id),
@ -118,10 +120,26 @@ RSpec.describe Issues::CreateService do
expect(issue.labels).to match_array(labels)
expect(issue.milestone).to eq(milestone)
expect(issue.due_date).to eq(Date.tomorrow)
expect(issue.work_item_type.base_type).to eq('issue')
expect(issue.work_item_type.base_type).to eq('task')
expect(issue.issue_customer_relations_contacts).to be_empty
end
context 'when the work item type is not allowed to create' do
before do
allow_next_instance_of(::Issues::BuildService) do |instance|
allow(instance).to receive(:create_issue_type_allowed?).twice.and_return(false)
end
end
it 'ignores the type and creates default issue' do
expect(result).to be_success
expect(issue).to be_persisted
expect(issue).to be_a(::Issue)
expect(issue.work_item_type.base_type).to eq('issue')
expect(issue.issue_type).to eq('issue')
end
end
it 'calls NewIssueWorker with correct arguments' do
expect(NewIssueWorker).to receive(:perform_async).with(Integer, user.id, 'Issue')

View File

@ -74,6 +74,14 @@ RSpec.describe Issues::ReopenService do
it_behaves_like 'an incident management tracked event', :incident_management_incident_reopened
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { issue.namespace }
let(:category) { described_class.to_s }
let(:action) { 'incident_management_incident_reopened' }
let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
end
it 'creates a timeline event' do
expect(IncidentManagement::TimelineEvents::CreateService)
.to receive(:reopen_incident)

View File

@ -60,7 +60,7 @@ RSpec.describe Issues::UpdateService, :mailer do
description: 'Also please fix',
assignee_ids: [user2.id],
state_event: 'close',
label_ids: [label.id],
label_ids: [label&.id],
due_date: Date.tomorrow,
discussion_locked: true,
severity: 'low',
@ -189,6 +189,27 @@ RSpec.describe Issues::UpdateService, :mailer do
subject { update_issue(confidential: true) }
it_behaves_like 'an incident management tracked event', :incident_management_incident_change_confidential
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { issue.namespace }
let(:category) { described_class.to_s }
let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
let(:action) { 'incident_management_incident_change_confidential' }
let(:opts) do
{
title: 'New title',
description: 'Also please fix',
assignee_ids: [user2.id],
state_event: 'close',
due_date: Date.tomorrow,
discussion_locked: true,
severity: 'low',
milestone_id: milestone.id,
add_contacts: [contact.email]
}
end
end
end
end
@ -673,6 +694,14 @@ RSpec.describe Issues::UpdateService, :mailer do
let(:current_user) { user }
it_behaves_like 'an incident management tracked event', :incident_management_incident_assigned
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { issue.namespace }
let(:category) { described_class.to_s }
let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
let(:action) { "incident_management_incident_assigned" }
end
end
end

View File

@ -95,6 +95,14 @@ RSpec.describe Issues::ZoomLinkService do
let(:current_user) { user }
it_behaves_like 'an incident management tracked event', :incident_management_incident_zoom_meeting
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { issue.namespace }
let(:category) { described_class.to_s }
let(:action) { 'incident_management_incident_zoom_meeting' }
let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
end
end
context 'with insufficient issue update permissions' do

View File

@ -102,6 +102,14 @@ RSpec.describe Notes::CreateService do
it_behaves_like 'an incident management tracked event', :incident_management_incident_comment do
let(:current_user) { user }
end
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { issue.namespace }
let(:category) { described_class.to_s }
let(:action) { 'incident_management_incident_comment' }
let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
end
end
describe 'event tracking', :snowplow do

View File

@ -109,7 +109,8 @@ RSpec.describe Packages::Debian::ProcessPackageFileService do
end
context 'with already processed package file' do
let!(:package_file) { create(:debian_package_file) }
let_it_be(:package_file) { create(:debian_package_file) }
let(:component_name) { 'main' }
it 'raise ArgumentError', :aggregate_failures do

View File

@ -209,6 +209,15 @@ RSpec.describe TodoService do
it_behaves_like 'an incident management tracked event', :incident_management_incident_todo do
let(:current_user) { john_doe }
end
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { project.namespace }
let(:category) { described_class.to_s }
let(:action) { 'incident_management_incident_todo' }
let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
let(:user) { john_doe }
end
end
end
@ -1251,6 +1260,19 @@ RSpec.describe TodoService do
end
describe '#create_member_access_request' do
context 'snowplow event tracking' do
it 'does not track snowplow event when todos are for access request for project', :snowplow do
user = create(:user)
project = create(:project)
requester = create(:project_member, project: project, user: assignee)
project.add_owner(user)
expect_no_snowplow_event
service.create_member_access_request(requester)
end
end
context 'when the group has more than 10 owners' do
it 'creates todos for 10 recently active group owners' do
group = create(:group, :public)

View File

@ -0,0 +1,138 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Packages::Debian::ProcessPackageFileWorker, type: :worker, feature_category: :package_registry do
let_it_be(:user) { create(:user) }
let_it_be_with_reload(:distribution) { create(:debian_project_distribution, :with_file, codename: 'unstable') }
let(:incoming) { create(:debian_incoming, project: distribution.project) }
let(:distribution_name) { distribution.codename }
let(:worker) { described_class.new }
describe '#perform' do
let(:package_file_id) { package_file.id }
let(:user_id) { user.id }
subject { worker.perform(package_file_id, user_id, distribution_name, component_name) }
shared_examples 'returns early without error' do
it 'returns early without error' do
expect(Gitlab::ErrorTracking).not_to receive(:log_exception)
expect(::Packages::Debian::ProcessPackageFileService).not_to receive(:new)
subject
end
end
using RSpec::Parameterized::TableSyntax
where(:case_name, :expected_file_type, :file_name, :component_name) do
'with a deb' | 'deb' | 'libsample0_1.2.3~alpha2_amd64.deb' | 'main'
'with an udeb' | 'udeb' | 'sample-udeb_1.2.3~alpha2_amd64.udeb' | 'contrib'
end
with_them do
context 'with Debian package file' do
let(:package_file) { incoming.package_files.with_file_name(file_name).first }
context 'with mocked service' do
it 'calls ProcessPackageFileService' do
expect(Gitlab::ErrorTracking).not_to receive(:log_exception)
expect_next_instance_of(::Packages::Debian::ProcessPackageFileService) do |service|
expect(service).to receive(:execute)
.with(no_args)
end
subject
end
end
context 'with non existing user' do
let(:user_id) { non_existing_record_id }
it_behaves_like 'returns early without error'
end
context 'with nil user id' do
let(:user_id) { nil }
it_behaves_like 'returns early without error'
end
context 'when the service raises an error' do
let(:package_file) { incoming.package_files.with_file_name('sample_1.2.3~alpha2.tar.xz').first }
it 'removes package file', :aggregate_failures do
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
instance_of(ArgumentError),
package_file_id: package_file_id,
user_id: user_id,
distribution_name: distribution_name,
component_name: component_name
)
expect { subject }
.to not_change(Packages::Package, :count)
.and change { Packages::PackageFile.count }.by(-1)
.and change { incoming.package_files.count }.from(7).to(6)
expect { package_file.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end
it_behaves_like 'an idempotent worker' do
let(:job_args) { [package_file.id, user.id, distribution_name, component_name] }
it 'sets the Debian file type as deb', :aggregate_failures do
expect(Gitlab::ErrorTracking).not_to receive(:log_exception)
# Using subject inside this block will process the job multiple times
expect { subject }
.to change { Packages::Package.count }.from(1).to(2)
.and not_change(Packages::PackageFile, :count)
.and change { incoming.package_files.count }.from(7).to(6)
.and change {
package_file&.debian_file_metadatum&.reload&.file_type
}.from('unknown').to(expected_file_type)
created_package = Packages::Package.last
expect(created_package.name).to eq 'sample'
expect(created_package.version).to eq '1.2.3~alpha2'
expect(created_package.creator).to eq user
end
end
end
end
context 'with already processed package file' do
let_it_be(:package_file) { create(:debian_package_file) }
let(:component_name) { 'main' }
it_behaves_like 'returns early without error'
end
context 'with a deb' do
let(:package_file) { incoming.package_files.with_file_name('libsample0_1.2.3~alpha2_amd64.deb').first }
let(:component_name) { 'main' }
context 'with FIPS mode enabled', :fips_mode do
it 'raises an error' do
expect { subject }.to raise_error(::Packages::FIPS::DisabledError)
end
end
context 'with non existing package file' do
let(:package_file_id) { non_existing_record_id }
it_behaves_like 'returns early without error'
end
context 'with nil package file id' do
let(:package_file_id) { nil }
it_behaves_like 'returns early without error'
end
end
end
end

View File

@ -67,11 +67,8 @@ func rewriteFormFilesFromMultipart(r *http.Request, writer *multipart.Writer, fi
// Create multipart reader
reader, err := r.MultipartReader()
if err != nil {
if err == http.ErrNotMultipart {
// We want to be able to recognize http.ErrNotMultipart elsewhere so no fmt.Errorf
return http.ErrNotMultipart
}
return fmt.Errorf("get multipart reader: %v", err)
// We want to be able to recognize these errors elsewhere so no fmt.Errorf
return err
}
multipartUploadRequests.WithLabelValues(filter.Name()).Inc()

View File

@ -51,7 +51,7 @@ func interceptMultipartFiles(w http.ResponseWriter, r *http.Request, h http.Hand
err := rewriteFormFilesFromMultipart(r, writer, filter, fa, p)
if err != nil {
switch err {
case ErrInjectedClientParam:
case ErrInjectedClientParam, http.ErrMissingBoundary:
helper.CaptureAndFail(w, r, err, "Bad Request", http.StatusBadRequest)
case ErrTooManyFilesUploaded:
helper.CaptureAndFail(w, r, err, err.Error(), http.StatusBadRequest)

View File

@ -352,6 +352,18 @@ func TestInvalidFileNames(t *testing.T) {
}
}
func TestBadMultipartHeader(t *testing.T) {
httpRequest, err := http.NewRequest("POST", "/example", bytes.NewReader(nil))
require.NoError(t, err)
// Invalid header: missing boundary
httpRequest.Header.Set("Content-Type", "multipart/form-data")
response := httptest.NewRecorder()
testInterceptMultipartFiles(t, response, httpRequest, nilHandler, &SavedFileTracker{Request: httpRequest})
require.Equal(t, 400, response.Code)
}
func TestContentDispositionRewrite(t *testing.T) {
testhelper.ConfigureSecret()