Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-10-04 15:09:33 +00:00
parent 7928b47c8e
commit bf6d126a58
67 changed files with 849 additions and 206 deletions

View File

@ -2,6 +2,12 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 15.4.2 (2022-10-04)
### Fixed (1 change)
- [Ensure that stage name and record are in sync for page deployments](gitlab-org/gitlab@ce58ec2ef959bd35ddbc6992560a163c8fc4f145) ([merge request](gitlab-org/gitlab!100037))
## 15.4.1 (2022-09-29)
### Security (15 changes)

View File

@ -1 +1 @@
e02759b6d805b0eadfeba21a67bec6de75f9a750
5c49f857a37ee47158e2caff7c963e7de9563e8b

View File

@ -280,6 +280,7 @@ export function renderTableRow(state, node) {
}
export function renderTable(state, node) {
state.flushClose();
setIsInBlockTable(node, shouldRenderHTMLTable(node));
if (isInBlockTable(node)) renderTagOpen(state, 'table');

View File

@ -981,20 +981,20 @@ export default {
name="project[project_feature_attributes][feature_flags_access_level]"
/>
</project-setting-row>
<project-setting-row
ref="releases-settings"
:label="$options.i18n.releasesLabel"
:help-text="$options.i18n.releasesHelpText"
:help-path="releasesHelpPath"
>
<project-feature-setting
v-model="releasesAccessLevel"
:label="$options.i18n.releasesLabel"
:options="featureAccessLevelOptions"
name="project[project_feature_attributes][releases_access_level]"
/>
</project-setting-row>
</template>
<project-setting-row
ref="releases-settings"
:label="$options.i18n.releasesLabel"
:help-text="$options.i18n.releasesHelpText"
:help-path="releasesHelpPath"
>
<project-feature-setting
v-model="releasesAccessLevel"
:label="$options.i18n.releasesLabel"
:options="featureAccessLevelOptions"
name="project[project_feature_attributes][releases_access_level]"
/>
</project-setting-row>
</div>
<project-setting-row v-if="canDisableEmails" ref="email-settings" class="mb-3">
<label class="js-emails-disabled">

View File

@ -21,7 +21,9 @@ export const i18n = {
),
description: s__(
`SecurityConfiguration|Once you've enabled a scan for the default branch,
any subsequent feature branch you create will include the scan.`,
any subsequent feature branch you create will include the scan. An enabled
scanner will not be reflected as such until the pipeline has been
successfully executed and it has generated valid artifacts.`,
),
securityConfiguration: __('Security Configuration'),
vulnerabilityManagement: s__('SecurityConfiguration|Vulnerability Management'),

View File

@ -22,7 +22,7 @@ export default {
s__('SecurityConfiguration|High-level vulnerability statistics across projects and groups'),
s__('SecurityConfiguration|Runtime security metrics for application environments'),
s__(
'SecurityConfiguration|More scan types, including Container Scanning, DAST, Dependency Scanning, Fuzzing, and Licence Compliance',
'SecurityConfiguration|More scan types, including DAST, Dependency Scanning, Fuzzing, and Licence Compliance',
),
],
buttonText: s__('SecurityConfiguration|Upgrade or start a free trial'),

View File

@ -435,14 +435,14 @@ class ProjectsController < Projects::ApplicationController
analytics_access_level
security_and_compliance_access_level
container_registry_access_level
releases_access_level
] + operations_feature_attributes
end
def operations_feature_attributes
if Feature.enabled?(:split_operations_visibility_permissions, project)
%i[
environments_access_level feature_flags_access_level releases_access_level
monitor_access_level
environments_access_level feature_flags_access_level monitor_access_level
]
else
%i[operations_access_level]

View File

@ -10,4 +10,24 @@ class BulkImports::Failure < ApplicationRecord
optional: false
validates :entity, presence: true
def relation
pipeline_relation || default_relation
end
private
def pipeline_relation
klass = pipeline_class.constantize
return unless klass.ancestors.include?(BulkImports::Pipeline)
klass.relation
rescue NameError
nil
end
def default_relation
pipeline_class.demodulize.chomp('Pipeline').underscore
end
end

View File

@ -227,7 +227,7 @@ module Ci
yaml_variables when environment coverage_regex
description tag_list protected needs_attributes
job_variables_attributes resource_group scheduling_type
ci_stage partition_id].freeze
ci_stage partition_id id_tokens].freeze
end
end
@ -1204,6 +1204,14 @@ module Ci
end
def job_jwt_variables
if project.ci_cd_settings.opt_in_jwt?
id_tokens_variables
else
legacy_jwt_variables.concat(id_tokens_variables)
end
end
def legacy_jwt_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
break variables unless Feature.enabled?(:ci_job_jwt, project)
@ -1217,6 +1225,20 @@ module Ci
end
end
def id_tokens_variables
return [] unless id_tokens?
Gitlab::Ci::Variables::Collection.new.tap do |variables|
id_tokens.each do |var_name, token_data|
token = Gitlab::Ci::JwtV2.for_build(self, aud: token_data['id_token']['aud'])
variables.append(key: var_name, value: token, public: false, masked: true)
end
rescue OpenSSL::PKey::RSAError, Gitlab::Ci::Jwt::NoSigningKeyError => e
Gitlab::ErrorTracking.track_exception(e)
end
end
def cache_for_online_runners(&block)
Rails.cache.fetch(
['has-online-runners', id],

View File

@ -80,7 +80,7 @@ module Ci
end
def id_tokens?
!!metadata&.id_tokens?
metadata&.id_tokens.present?
end
def id_tokens=(value)

View File

@ -21,7 +21,7 @@ module Integrations
end
def help
s_("HarborIntegration|After the Harbor integration is activated, global variables '$HARBOR_USERNAME', '$HARBOR_HOST', '$HARBOR_OCI', '$HARBOR_PASSWORD', '$HARBOR_URL' and '$HARBOR_PROJECT' will be created for CI/CD use.")
s_("HarborIntegration|After the Harbor integration is activated, global variables $HARBOR_USERNAME, $HARBOR_HOST, $HARBOR_OCI, $HARBOR_PASSWORD, $HARBOR_URL and $HARBOR_PROJECT will be created for CI/CD use.")
end
def hostname

View File

@ -789,8 +789,8 @@ class Repository
Commit.order_by(collection: commits, order_by: order_by, sort: sort)
end
def branch_names_contains(sha)
raw_repository.branch_names_contains_sha(sha)
def branch_names_contains(sha, limit: 0)
raw_repository.branch_names_contains_sha(sha, limit: limit)
end
def tag_names_contains(sha, limit: 0)

View File

@ -399,7 +399,7 @@ class ProjectPolicy < BasePolicy
prevent(:admin_feature_flags_client)
end
rule { split_operations_visibility_permissions & releases_disabled }.policy do
rule { releases_disabled }.policy do
prevent(*create_read_update_admin_destroy(:release))
end

View File

@ -3,17 +3,43 @@ module Packages
module Rpm
module RepositoryMetadata
class BaseBuilder
def initialize(xml: nil, data: {})
@xml = Nokogiri::XML(xml) if xml.present?
@data = data
end
def execute
build_empty_structure
return build_empty_structure if xml.blank?
update_xml_document
update_package_count
xml.to_xml
end
private
attr_reader :xml, :data
def build_empty_structure
Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
xml.method_missing(self.class::ROOT_TAG, self.class::ROOT_ATTRIBUTES)
end.to_xml
end
def update_xml_document
# Add to the root xml element a new package metadata node
xml.at(self.class::ROOT_TAG).add_child(build_new_node)
end
def update_package_count
packages_count = xml.css("//#{self.class::ROOT_TAG}/package").count
xml.at(self.class::ROOT_TAG).attributes["packages"].value = packages_count.to_s
end
def build_new_node
raise NotImplementedError, "#{self.class} should implement #{__method__}"
end
end
end
end

View File

@ -9,6 +9,79 @@ module Packages
'xmlns:rpm': 'http://linux.duke.edu/metadata/rpm',
packages: '0'
}.freeze
# Nodes that have only text without attributes
REQUIRED_BASE_ATTRIBUTES = %i[name arch summary description].freeze
NOT_REQUIRED_BASE_ATTRIBUTES = %i[url packager].freeze
FORMAT_NODE_BASE_ATTRIBUTES = %i[license vendor group buildhost sourcerpm].freeze
private
def build_new_node
builder = Nokogiri::XML::Builder.new do |xml|
xml.package(type: :rpm, 'xmlns:rpm': 'http://linux.duke.edu/metadata/rpm') do
build_required_base_attributes(xml)
build_not_required_base_attributes(xml)
xml.version epoch: data[:epoch], ver: data[:version], rel: data[:release]
xml.checksum data[:checksum], type: 'sha256', pkgid: 'YES'
xml.size package: data[:packagesize], installed: data[:installedsize], archive: data[:archivesize]
xml.time file: data[:filetime], build: data[:buildtime]
xml.location href: data[:location] if data[:location].present?
build_format_node(xml)
end
end
Nokogiri::XML(builder.to_xml).at('package')
end
def build_required_base_attributes(xml)
REQUIRED_BASE_ATTRIBUTES.each do |attribute|
xml.method_missing(attribute, data[attribute])
end
end
def build_not_required_base_attributes(xml)
NOT_REQUIRED_BASE_ATTRIBUTES.each do |attribute|
xml.method_missing(attribute, data[attribute]) if data[attribute].present?
end
end
def build_format_node(xml)
xml.format do
build_base_format_attributes(xml)
build_provides_node(xml)
build_requires_node(xml)
end
end
def build_base_format_attributes(xml)
FORMAT_NODE_BASE_ATTRIBUTES.each do |attribute|
xml[:rpm].method_missing(attribute, data[attribute]) if data[attribute].present?
end
end
def build_requires_node(xml)
xml[:rpm].requires do
data[:requirements].each do |requires|
xml[:rpm].entry(
name: requires[:requirename],
flags: requires[:requireflags],
ver: requires[:requireversion]
)
end
end
end
def build_provides_node(xml)
xml[:rpm].provides do
data[:provides].each do |provides|
xml[:rpm].entry(
name: provides[:providename],
flags: provides[:provideflags],
ver: provides[:provideversion])
end
end
end
end
end
end

View File

@ -24,7 +24,8 @@
},
"additionalProperties": false
},
"^file$": { "type": "boolean" }
"^file$": { "type": "boolean" },
"^token$": { "type": "string" }
},
"additionalProperties": false
}

View File

@ -47,7 +47,7 @@ module BulkImports
return skip_tracker if pipeline_tracker.entity.failed?
raise(Pipeline::ExpiredError, 'Pipeline timeout') if job_timeout?
raise(Pipeline::FailedError, export_status.error) if export_failed?
raise(Pipeline::FailedError, "Export from source instance failed: #{export_status.error}") if export_failed?
return re_enqueue if export_empty? || export_started?

View File

@ -37,7 +37,9 @@ module Gitlab
importer_class.new(object, project, client).execute
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :imported)
if increment_object_counter?(object)
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :imported)
end
info(project.id, message: 'importer finished')
rescue NoMethodError => e
@ -49,6 +51,10 @@ module Gitlab
track_and_raise_exception(project, e)
end
def increment_object_counter?(_object)
true
end
def object_type
raise NotImplementedError
end

View File

@ -16,6 +16,10 @@ module Gitlab
def object_type
:issue
end
def increment_object_counter?(object)
!object.pull_request?
end
end
end
end

View File

@ -150,11 +150,14 @@ curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab
"updated_at": "2021-06-24T10:40:46.590Z",
"failures": [
{
"pipeline_class": "BulkImports::Groups::Pipelines::GroupPipeline",
"pipeline_step": "extractor",
"relation": "group",
"step": "extractor",
"exception_message": "Error!",
"exception_class": "Exception",
"correlation_id_value": "dfcf583058ed4508e4c7c617bd7f0edd",
"created_at": "2021-06-24T10:40:46.495Z"
"created_at": "2021-06-24T10:40:46.495Z",
"pipeline_class": "BulkImports::Groups::Pipelines::GroupPipeline",
"pipeline_step": "extractor"
}
]
}

View File

@ -1229,6 +1229,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your-token>" \
| `pages_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, `enabled`, or `public`. |
| `printing_merge_request_link_enabled` | boolean | **{dotted-circle}** No | Show link to create/view merge request when pushing from the command line. |
| `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project members. |
| `releases_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `remove_source_branch_after_merge` | boolean | **{dotted-circle}** No | Enable `Delete source branch` option by default for all new merge requests. |
| `repository_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `repository_storage` | string | **{dotted-circle}** No | Which storage shard the repository is on. _(administrator only)_ |
@ -1307,6 +1308,7 @@ POST /projects/user/:user_id
| `path` | string | **{dotted-circle}** No | Custom repository name for new project. By default generated based on name. |
| `printing_merge_request_link_enabled` | boolean | **{dotted-circle}** No | Show link to create/view merge request when pushing from the command line. |
| `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project-members. |
| `releases_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `remove_source_branch_after_merge` | boolean | **{dotted-circle}** No | Enable `Delete source branch` option by default for all new merge requests. |
| `repository_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `repository_storage` | string | **{dotted-circle}** No | Which storage shard the repository is on. _(administrators only)_ |
@ -1408,6 +1410,7 @@ Supported attributes:
| `path` | string | **{dotted-circle}** No | Custom repository name for the project. By default generated based on name. |
| `printing_merge_request_link_enabled` | boolean | **{dotted-circle}** No | Show link to create/view merge request when pushing from the command line. |
| `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project members. |
| `releases_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `remove_source_branch_after_merge` | boolean | **{dotted-circle}** No | Enable `Delete source branch` option by default for all new merge requests. |
| `repository_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `repository_storage` | string | **{dotted-circle}** No | Which storage shard the repository is on. _(administrators only)_ |

View File

@ -92,7 +92,8 @@ Use the toggles to enable or disable features in the project.
| **Snippets** | ✓ | Enables [sharing of code and text](../../snippets.md). |
| **Pages** | ✓ | Allows you to [publish static websites](../pages/index.md). |
| **Operations** | ✓ | Control access to Operations-related features, including [Operations Dashboard](../../../operations/index.md), [Environments and Deployments](../../../ci/environments/index.md), [Feature Flags](../../../operations/feature_flags.md). |
| **Metrics Dashboard** | ✓ | Control access to [metrics dashboard](../integrations/prometheus.md). |
| **Metrics Dashboard** | ✓ | Control access to [metrics dashboard](../integrations/prometheus.md). |
| **Releases** | ✓ | Control access to [Releases](../releases/index.md). |
When you disable a feature, the following additional features are also disabled:

View File

@ -4,14 +4,16 @@ module API
module Entities
module BulkImports
class EntityFailure < Grape::Entity
expose :relation
expose :pipeline_step, as: :step
expose :exception_message do |failure|
::Projects::ImportErrorFilter.filter_message(failure.exception_message.truncate(72))
end
expose :exception_class
expose :pipeline_class
expose :pipeline_step
expose :correlation_id_value
expose :created_at
expose :pipeline_class
expose :pipeline_step
end
end
end

View File

@ -80,6 +80,7 @@ module API
expose(:analytics_access_level) { |project, options| project_feature_string_access_level(project, :analytics) }
expose(:container_registry_access_level) { |project, options| project_feature_string_access_level(project, :container_registry) }
expose(:security_and_compliance_access_level) { |project, options| project_feature_string_access_level(project, :security_and_compliance) }
expose(:releases_access_level) { |project, options| project_feature_string_access_level(project, :releases) }
expose :emails_disabled
expose :shared_runners_enabled

View File

@ -40,6 +40,8 @@ module API
end
put 'authorize' do
project = authorized_user_project
authorize_workhorse!(subject: project, maximum_size: project.actual_limits.generic_packages_max_file_size)
end
@ -59,6 +61,8 @@ module API
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
put do
project = authorized_user_project
authorize_upload!(project)
bad_request!('File is too large') if max_file_size_exceeded?
@ -95,6 +99,8 @@ module API
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
get do
project = authorized_user_project(action: :read_package)
authorize_read_package!(project)
package = ::Packages::Generic::PackageFinder.new(project).execute!(params[:package_name], params[:package_version])
@ -112,12 +118,8 @@ module API
include ::API::Helpers::PackagesHelpers
include ::API::Helpers::Packages::BasicAuthHelpers
def project
authorized_user_project
end
def max_file_size_exceeded?
project.actual_limits.exceeded?(:generic_packages_max_file_size, params[:file].size)
authorized_user_project.actual_limits.exceeded?(:generic_packages_max_file_size, params[:file].size)
end
end
end

View File

@ -14,15 +14,27 @@ module API
include Constants
include Gitlab::Utils::StrongMemoize
def authorized_user_project
@authorized_user_project ||= authorized_project_find!
def authorized_user_project(action: :read_project)
strong_memoize("authorized_user_project_#{action}") do
authorized_project_find!(action: action)
end
end
def authorized_project_find!
def authorized_project_find!(action: :read_project)
project = find_project(params[:id])
unless project && can?(current_user, :read_project, project)
return unauthorized_or! { not_found! }
return unauthorized_or! { not_found! } unless project
case action
when :read_package
unless can?(current_user, :read_package, project&.packages_policy_subject)
# guest users can have :read_project but not :read_package
return forbidden! if can?(current_user, :read_project, project)
return unauthorized_or! { not_found! }
end
else
return unauthorized_or! { not_found! } unless can?(current_user, action, project)
end
project

View File

@ -36,6 +36,7 @@ module API
optional :analytics_access_level, type: String, values: %w(disabled private enabled), desc: 'Analytics access level. One of `disabled`, `private` or `enabled`'
optional :container_registry_access_level, type: String, values: %w(disabled private enabled), desc: 'Controls visibility of the container registry. One of `disabled`, `private` or `enabled`. `private` will make the container registry accessible only to project members (reporter role and above). `enabled` will make the container registry accessible to everyone who has access to the project. `disabled` will disable the container registry'
optional :security_and_compliance_access_level, type: String, values: %w(disabled private enabled), desc: 'Security and compliance access level. One of `disabled`, `private` or `enabled`'
optional :releases_access_level, type: String, values: %w(disabled private enabled), desc: 'Releases access level. One of `disabled`, `private` or `enabled`'
optional :emails_disabled, type: Boolean, desc: 'Disable email notifications'
optional :show_default_award_emojis, type: Boolean, desc: 'Show default award emojis'
@ -179,6 +180,7 @@ module API
:keep_latest_artifact,
:mr_default_target_self,
:enforce_auth_checks_on_uploads,
:releases_access_level,
# TODO: remove in API v5, replaced by *_access_level
:issues_enabled,

View File

@ -183,7 +183,11 @@ module BulkImports
end
def relation
class_attributes[:relation_name]
class_attributes[:relation_name] || default_relation
end
def default_relation
self.name.demodulize.chomp('Pipeline').underscore
end
private

View File

@ -12,7 +12,7 @@ module Gitlab
self.new(build, ttl: build.metadata_timeout).encoded
end
def initialize(build, ttl: nil)
def initialize(build, ttl:)
@build = build
@ttl = ttl
end

View File

@ -3,13 +3,27 @@
module Gitlab
module Ci
class JwtV2 < Jwt
DEFAULT_AUD = Settings.gitlab.base_url
def self.for_build(build, aud: DEFAULT_AUD)
new(build, ttl: build.metadata_timeout, aud: aud).encoded
end
def initialize(build, ttl:, aud:)
super(build, ttl: ttl)
@aud = aud
end
private
attr_reader :aud
def reserved_claims
super.merge(
iss: Settings.gitlab.base_url,
sub: "project_path:#{project.full_path}:ref_type:#{ref_type}:ref:#{source_ref}",
aud: Settings.gitlab.base_url
aud: aud
)
end
end

View File

@ -1027,8 +1027,8 @@ module Gitlab
@praefect_info_client ||= Gitlab::GitalyClient::PraefectInfoService.new(self)
end
def branch_names_contains_sha(sha)
gitaly_ref_client.branch_names_contains_sha(sha)
def branch_names_contains_sha(sha, limit: 0)
gitaly_ref_client.branch_names_contains_sha(sha, limit: limit)
end
def tag_names_contains_sha(sha, limit: 0)

View File

@ -40,6 +40,10 @@ module Gitlab
{ state: 'all', sort: 'created', direction: 'asc' }
end
def increment_object_counter?(object)
object[:pull_request].nil?
end
private
def additional_object_data

View File

@ -129,7 +129,9 @@ module Gitlab
next if already_imported?(object)
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
if increment_object_counter?(object)
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
end
yield object
@ -140,6 +142,10 @@ module Gitlab
end
end
def increment_object_counter?(_object)
true
end
# Returns true if the given object has already been imported, false
# otherwise.
#

View File

@ -14,11 +14,13 @@ module Gitlab
PROC_SMAPS_ROLLUP_PATH = '/proc/%s/smaps_rollup'
PROC_LIMITS_PATH = '/proc/self/limits'
PROC_FD_GLOB = '/proc/self/fd/*'
PROC_MEM_INFO = '/proc/meminfo'
PRIVATE_PAGES_PATTERN = /^(Private_Clean|Private_Dirty|Private_Hugetlb):\s+(?<value>\d+)/.freeze
PSS_PATTERN = /^Pss:\s+(?<value>\d+)/.freeze
RSS_PATTERN = /VmRSS:\s+(?<value>\d+)/.freeze
MAX_OPEN_FILES_PATTERN = /Max open files\s*(?<value>\d+)/.freeze
MEM_TOTAL_PATTERN = /^MemTotal:\s+(?<value>\d+) (.+)/.freeze
def summary
proportional_mem = memory_usage_uss_pss
@ -45,6 +47,10 @@ module Gitlab
.transform_values(&:kilobytes)
end
def memory_total
sum_matches(PROC_MEM_INFO, memory_total: MEM_TOTAL_PATTERN)[:memory_total].kilobytes
end
def file_descriptor_count
Dir.glob(PROC_FD_GLOB).length
end

View File

@ -51,9 +51,10 @@ module Gitlab
def refresh_state(phase)
@phase = PHASE.fetch(phase)
@current_rss = get_rss
@soft_limit_rss = get_soft_limit_rss
@hard_limit_rss = get_hard_limit_rss
@current_rss = get_rss_kb
@soft_limit_rss = get_soft_limit_rss_kb
@hard_limit_rss = get_hard_limit_rss_kb
@memory_total = get_memory_total_kb
# track the current state as prometheus gauges
@metrics[:sidekiq_memory_killer_phase].set({}, @phase)
@ -178,6 +179,7 @@ module Gitlab
current_rss: @current_rss,
soft_limit_rss: @soft_limit_rss,
hard_limit_rss: @hard_limit_rss,
memory_total_kb: @memory_total,
reason: reason,
running_jobs: running_jobs)
@ -214,18 +216,19 @@ module Gitlab
end
end
def get_rss
output, status = Gitlab::Popen.popen(%W(ps -o rss= -p #{pid}), Rails.root.to_s)
return 0 unless status&.zero?
output.to_i
def get_memory_total_kb
Gitlab::Metrics::System.memory_total / 1.kilobytes
end
def get_soft_limit_rss
def get_rss_kb
Gitlab::Metrics::System.memory_usage_rss / 1.kilobytes
end
def get_soft_limit_rss_kb
SOFT_LIMIT_RSS_KB + rss_increase_by_jobs
end
def get_hard_limit_rss
def get_hard_limit_rss_kb
HARD_LIMIT_RSS_KB
end

View File

@ -19473,7 +19473,7 @@ msgstr ""
msgid "Harbor Registry"
msgstr ""
msgid "HarborIntegration|After the Harbor integration is activated, global variables '$HARBOR_USERNAME', '$HARBOR_HOST', '$HARBOR_OCI', '$HARBOR_PASSWORD', '$HARBOR_URL' and '$HARBOR_PROJECT' will be created for CI/CD use."
msgid "HarborIntegration|After the Harbor integration is activated, global variables $HARBOR_USERNAME, $HARBOR_HOST, $HARBOR_OCI, $HARBOR_PASSWORD, $HARBOR_URL and $HARBOR_PROJECT will be created for CI/CD use."
msgstr ""
msgid "HarborIntegration|Base URL of the Harbor instance."
@ -35649,13 +35649,13 @@ msgstr ""
msgid "SecurityConfiguration|Manage profiles for use by DAST scans."
msgstr ""
msgid "SecurityConfiguration|More scan types, including Container Scanning, DAST, Dependency Scanning, Fuzzing, and Licence Compliance"
msgid "SecurityConfiguration|More scan types, including DAST, Dependency Scanning, Fuzzing, and Licence Compliance"
msgstr ""
msgid "SecurityConfiguration|Not enabled"
msgstr ""
msgid "SecurityConfiguration|Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgid "SecurityConfiguration|Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan. An enabled scanner will not be reflected as such until the pipeline has been successfully executed and it has generated valid artifacts."
msgstr ""
msgid "SecurityConfiguration|Quickly enable all continuous testing and compliance tools by enabling %{linkStart}Auto DevOps%{linkEnd}"
@ -36291,7 +36291,7 @@ msgstr ""
msgid "SecurityReports|Status"
msgstr ""
msgid "SecurityReports|Still Detected"
msgid "SecurityReports|Still detected"
msgstr ""
msgid "SecurityReports|Submit vulnerability"

View File

@ -54,7 +54,7 @@ module QA
def verify_status_data
stats = imported_project.project_import_status.dig(:stats, :imported)
expect(stats).to include(
# issue: 2,
issue: 1,
label: 9,
milestone: 1,
note: 3,

View File

@ -2,7 +2,10 @@
module QA
RSpec.describe 'Create' do
describe 'Repository License Detection', product_group: :source_code do
describe 'Repository License Detection', product_group: :source_code, quarantine: {
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/376154',
type: :stale
} do
after do
project.remove_via_api!
end

47
spec/fixtures/packages/rpm/payload.json vendored Normal file
View File

@ -0,0 +1,47 @@
{
"files": [
"/usr/bin/hello.sh"
],
"changelogs": [
{
"changelogtext": "First build",
"changelogtime": 1662552000
}
],
"requirements": [
{
"requirename": "bash",
"requireversion": "",
"requireflags": 0
},
{
"requirename": "rpmlib(CompressedFileNames)",
"requireversion": "3.0.4-1",
"requireflags": 16777226
}
],
"provides": [
{
"providename": "hello",
"provideflags": 8,
"provideversion": "0.0.1-1.fc29"
},
{
"providename": "hello(x86-64)",
"provideflags": 8,
"provideversion": "0.0.1-1.fc29"
}
],
"name": "hello",
"version": "0.0.1",
"release": "1.fc29",
"summary": "Simple RPM package",
"description": "Fake rpm package",
"arch": "x86_64",
"license": "MIT",
"sourcerpm": "hello-0.0.1-1.fc29.src.rpm",
"group": "Unspecified",
"buildhost": "localhost",
"packager": null,
"vendor": null
}

View File

@ -1,40 +1,36 @@
import Vue, { nextTick } from 'vue';
import { mountComponentWithStore } from 'helpers/vue_mount_component_helper';
import { nextTick } from 'vue';
import { mount } from '@vue/test-utils';
import DraftsCount from '~/batch_comments/components/drafts_count.vue';
import { createStore } from '~/batch_comments/stores';
describe('Batch comments drafts count component', () => {
let vm;
let Component;
beforeAll(() => {
Component = Vue.extend(DraftsCount);
});
let store;
let wrapper;
beforeEach(() => {
const store = createStore();
store = createStore();
store.state.batchComments.drafts.push('comment');
vm = mountComponentWithStore(Component, { store });
wrapper = mount(DraftsCount, { store });
});
afterEach(() => {
vm.$destroy();
wrapper.destroy();
});
it('renders count', () => {
expect(vm.$el.textContent).toContain('1');
expect(wrapper.text()).toContain('1');
});
it('renders screen reader text', async () => {
const el = vm.$el.querySelector('.sr-only');
const el = wrapper.find('.sr-only');
expect(el.textContent).toContain('draft');
vm.$store.state.batchComments.drafts.push('comment 2');
expect(el.text()).toContain('draft');
store.state.batchComments.drafts.push('comment 2');
await nextTick();
expect(el.textContent).toContain('drafts');
expect(el.text()).toContain('drafts');
});
});

View File

@ -1,5 +1,4 @@
import Vue from 'vue';
import { mountComponentWithStore } from 'helpers/vue_mount_component_helper';
import { mount } from '@vue/test-utils';
import PreviewItem from '~/batch_comments/components/preview_item.vue';
import { createStore } from '~/batch_comments/stores';
import diffsModule from '~/diffs/store/modules';
@ -8,8 +7,7 @@ import '~/behaviors/markdown/render_gfm';
import { createDraft } from '../mock_data';
describe('Batch comments draft preview item component', () => {
let vm;
let Component;
let wrapper;
let draft;
function createComponent(isLast = false, extra = {}, extendStore = () => {}) {
@ -24,21 +22,17 @@ describe('Batch comments draft preview item component', () => {
...extra,
};
vm = mountComponentWithStore(Component, { store, props: { draft, isLast } });
wrapper = mount(PreviewItem, { store, propsData: { draft, isLast } });
}
beforeAll(() => {
Component = Vue.extend(PreviewItem);
});
afterEach(() => {
vm.$destroy();
wrapper.destroy();
});
it('renders text content', () => {
createComponent(false, { note_html: '<img src="" /><p>Hello world</p>' });
expect(vm.$el.querySelector('.review-preview-item-content').innerHTML).toEqual(
expect(wrapper.find('.review-preview-item-content').element.innerHTML).toBe(
'<p>Hello world</p>',
);
});
@ -47,9 +41,7 @@ describe('Batch comments draft preview item component', () => {
it('renders file path', () => {
createComponent(false, { file_path: 'index.js', file_hash: 'abc', position: {} });
expect(vm.$el.querySelector('.review-preview-item-header-text').textContent).toContain(
'index.js',
);
expect(wrapper.find('.review-preview-item-header-text').text()).toContain('index.js');
});
it('renders new line position', () => {
@ -66,7 +58,7 @@ describe('Batch comments draft preview item component', () => {
},
});
expect(vm.$el.querySelector('.bold').textContent).toContain(':+1');
expect(wrapper.find('.bold').text()).toContain(':+1');
});
it('renders old line position', () => {
@ -82,7 +74,7 @@ describe('Batch comments draft preview item component', () => {
},
});
expect(vm.$el.querySelector('.bold').textContent).toContain(':2');
expect(wrapper.find('.bold').text()).toContain(':2');
});
it('renders image position', () => {
@ -92,7 +84,7 @@ describe('Batch comments draft preview item component', () => {
position: { position_type: 'image', x: 10, y: 20 },
});
expect(vm.$el.querySelector('.bold').textContent).toContain('10x 20y');
expect(wrapper.find('.bold').text()).toContain('10x 20y');
});
});
@ -113,15 +105,13 @@ describe('Batch comments draft preview item component', () => {
});
it('renders title', () => {
expect(vm.$el.querySelector('.review-preview-item-header-text').textContent).toContain(
expect(wrapper.find('.review-preview-item-header-text').text()).toContain(
"Author 'Nick' Name's thread",
);
});
it('renders thread resolved text', () => {
expect(vm.$el.querySelector('.draft-note-resolution').textContent).toContain(
'Thread will be resolved',
);
expect(wrapper.find('.draft-note-resolution').text()).toContain('Thread will be resolved');
});
});
@ -131,9 +121,7 @@ describe('Batch comments draft preview item component', () => {
store.state.notes.discussions.push({});
});
expect(vm.$el.querySelector('.review-preview-item-header-text').textContent).toContain(
'Your new comment',
);
expect(wrapper.find('.review-preview-item-header-text').text()).toContain('Your new comment');
});
});
});

View File

@ -1,38 +1,34 @@
import Vue, { nextTick } from 'vue';
import { mountComponentWithStore } from 'helpers/vue_mount_component_helper';
import { nextTick } from 'vue';
import { mount } from '@vue/test-utils';
import PublishButton from '~/batch_comments/components/publish_button.vue';
import { createStore } from '~/batch_comments/stores';
describe('Batch comments publish button component', () => {
let vm;
let Component;
beforeAll(() => {
Component = Vue.extend(PublishButton);
});
let wrapper;
let store;
beforeEach(() => {
const store = createStore();
store = createStore();
vm = mountComponentWithStore(Component, { store, props: { shouldPublish: true } });
wrapper = mount(PublishButton, { store, propsData: { shouldPublish: true } });
jest.spyOn(vm.$store, 'dispatch').mockImplementation();
jest.spyOn(store, 'dispatch').mockImplementation();
});
afterEach(() => {
vm.$destroy();
wrapper.destroy();
});
it('dispatches publishReview on click', () => {
vm.$el.click();
it('dispatches publishReview on click', async () => {
await wrapper.trigger('click');
expect(vm.$store.dispatch).toHaveBeenCalledWith('batchComments/publishReview', undefined);
expect(store.dispatch).toHaveBeenCalledWith('batchComments/publishReview', undefined);
});
it('sets loading when isPublishing is true', async () => {
vm.$store.state.batchComments.isPublishing = true;
store.state.batchComments.isPublishing = true;
await nextTick();
expect(vm.$el.getAttribute('disabled')).toBe('disabled');
expect(wrapper.attributes('disabled')).toBe('disabled');
});
});

View File

@ -1204,6 +1204,24 @@ Oranges are orange [^1]
);
});
it('correctly adds a space between a preceding block element and a markdown table', () => {
expect(
serialize(
bulletList(listItem(paragraph('List item 1')), listItem(paragraph('List item 2'))),
table(tableRow(tableHeader(paragraph('header'))), tableRow(tableCell(paragraph('cell')))),
).trim(),
).toBe(
`
* List item 1
* List item 2
| header |
|--------|
| cell |
`.trim(),
);
});
it('correctly serializes reference definition', () => {
expect(
serialize(

View File

@ -79,7 +79,7 @@ describe('UpgradeBanner component', () => {
expect(wrapperText).toContain('statistics in the merge request');
expect(wrapperText).toContain('statistics across projects');
expect(wrapperText).toContain('Runtime security metrics');
expect(wrapperText).toContain('More scan types, including Container Scanning,');
expect(wrapperText).toContain('More scan types, including DAST,');
});
describe('when user interacts', () => {

View File

@ -9,8 +9,8 @@ RSpec.describe API::Entities::BulkImports::EntityFailure do
it 'has the correct attributes' do
expect(subject).to include(
:pipeline_class,
:pipeline_step,
:relation,
:step,
:exception_class,
:exception_message,
:correlation_id_value,

View File

@ -20,16 +20,17 @@ RSpec.describe BulkImports::Pipeline do
loader BulkImports::Loader, foo: :bar
end
stub_const('BulkImports::MyPipeline', klass)
stub_const('BulkImports::TestWikiPipeline', klass)
end
describe 'pipeline attributes' do
describe 'getters' do
it 'retrieves class attributes' do
expect(BulkImports::MyPipeline.get_extractor).to eq({ klass: BulkImports::Extractor, options: { foo: :bar } })
expect(BulkImports::MyPipeline.transformers).to contain_exactly({ klass: BulkImports::Transformer, options: { foo: :bar } })
expect(BulkImports::MyPipeline.get_loader).to eq({ klass: BulkImports::Loader, options: { foo: :bar } })
expect(BulkImports::MyPipeline.abort_on_failure?).to eq(true)
expect(BulkImports::TestWikiPipeline.get_extractor).to eq({ klass: BulkImports::Extractor, options: { foo: :bar } })
expect(BulkImports::TestWikiPipeline.transformers).to contain_exactly({ klass: BulkImports::Transformer, options: { foo: :bar } })
expect(BulkImports::TestWikiPipeline.get_loader).to eq({ klass: BulkImports::Loader, options: { foo: :bar } })
expect(BulkImports::TestWikiPipeline.abort_on_failure?).to eq(true)
expect(BulkImports::TestWikiPipeline.relation).to eq('test_wiki')
end
context 'when extractor and loader are defined within the pipeline' do
@ -59,23 +60,23 @@ RSpec.describe BulkImports::Pipeline do
klass = Class.new
options = { test: :test }
BulkImports::MyPipeline.extractor(klass, options)
BulkImports::MyPipeline.transformer(klass, options)
BulkImports::MyPipeline.loader(klass, options)
BulkImports::MyPipeline.abort_on_failure!
BulkImports::MyPipeline.file_extraction_pipeline!
BulkImports::TestWikiPipeline.extractor(klass, options)
BulkImports::TestWikiPipeline.transformer(klass, options)
BulkImports::TestWikiPipeline.loader(klass, options)
BulkImports::TestWikiPipeline.abort_on_failure!
BulkImports::TestWikiPipeline.file_extraction_pipeline!
expect(BulkImports::MyPipeline.get_extractor).to eq({ klass: klass, options: options })
expect(BulkImports::TestWikiPipeline.get_extractor).to eq({ klass: klass, options: options })
expect(BulkImports::MyPipeline.transformers)
expect(BulkImports::TestWikiPipeline.transformers)
.to contain_exactly(
{ klass: BulkImports::Transformer, options: { foo: :bar } },
{ klass: klass, options: options })
expect(BulkImports::MyPipeline.get_loader).to eq({ klass: klass, options: options })
expect(BulkImports::TestWikiPipeline.get_loader).to eq({ klass: klass, options: options })
expect(BulkImports::MyPipeline.abort_on_failure?).to eq(true)
expect(BulkImports::MyPipeline.file_extraction_pipeline?).to eq(true)
expect(BulkImports::TestWikiPipeline.abort_on_failure?).to eq(true)
expect(BulkImports::TestWikiPipeline.file_extraction_pipeline?).to eq(true)
end
end
end
@ -87,7 +88,7 @@ RSpec.describe BulkImports::Pipeline do
expect(BulkImports::Transformer).to receive(:new).with(foo: :bar)
expect(BulkImports::Loader).to receive(:new).with(foo: :bar)
pipeline = BulkImports::MyPipeline.new(context)
pipeline = BulkImports::TestWikiPipeline.new(context)
pipeline.send(:extractor)
pipeline.send(:transformers)

View File

@ -7,6 +7,8 @@ RSpec.describe Gitlab::Ci::JwtV2 do
let(:project) { build_stubbed(:project, namespace: namespace) }
let(:user) { build_stubbed(:user) }
let(:pipeline) { build_stubbed(:ci_pipeline, ref: 'auto-deploy-2020-03-19') }
let(:aud) { described_class::DEFAULT_AUD }
let(:build) do
build_stubbed(
:ci_build,
@ -16,7 +18,7 @@ RSpec.describe Gitlab::Ci::JwtV2 do
)
end
subject(:ci_job_jwt_v2) { described_class.new(build, ttl: 30) }
subject(:ci_job_jwt_v2) { described_class.new(build, ttl: 30, aud: aud) }
it { is_expected.to be_a Gitlab::Ci::Jwt }
@ -30,5 +32,13 @@ RSpec.describe Gitlab::Ci::JwtV2 do
expect(payload[:sub]).to eq("project_path:#{project.full_path}:ref_type:branch:ref:#{pipeline.source_ref}")
end
end
context 'when given an aud' do
let(:aud) { 'AWS' }
it 'uses that aud in the payload' do
expect(payload[:aud]).to eq('AWS')
end
end
end
end

View File

@ -483,6 +483,12 @@ RSpec.describe Gitlab::Git::Repository do
it 'displays that branch' do
expect(repository.branch_names_contains_sha(head_id)).to include('master', new_branch, utf8_branch)
end
context 'when limit is provided' do
it 'displays limited number of branches' do
expect(repository.branch_names_contains_sha(head_id, limit: 1)).to match_array(['2-mb-file'])
end
end
end
describe "#refs_hash" do

View File

@ -109,4 +109,24 @@ RSpec.describe Gitlab::GithubImport::Importer::IssuesImporter do
.to eq(42)
end
end
describe '#increment_object_counter?' do
let(:importer) { described_class.new(project, client) }
context 'when issue is a pull request' do
let(:github_issue) { { pull_request: { url: 'some_url' } } }
it 'returns false' do
expect(importer).not_to be_increment_object_counter(github_issue)
end
end
context 'when issue is a regular issue' do
let(:github_issue) { {} }
it 'returns true' do
expect(importer).to be_increment_object_counter(github_issue)
end
end
end
end

View File

@ -310,6 +310,7 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
describe '#each_object_to_import' do
let(:importer) { importer_class.new(project, client) }
let(:object) { {} }
let(:object_counter_class) { Gitlab::GithubImport::ObjectCounter }
before do
expect(importer)
@ -335,6 +336,9 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
.with(object)
.and_return(false)
expect(object_counter_class)
.to receive(:increment)
expect(importer)
.to receive(:mark_as_imported)
.with(object)
@ -365,6 +369,9 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
.with(object)
.and_return(false)
expect(object_counter_class)
.to receive(:increment)
expect(importer)
.to receive(:mark_as_imported)
.with(object)
@ -408,6 +415,9 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
.with(object)
.and_return(true)
expect(object_counter_class)
.not_to receive(:increment)
expect(importer)
.not_to receive(:mark_as_imported)
@ -464,4 +474,13 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
importer.mark_as_imported(object)
end
end
describe '#increment_object_counter?' do
let(:github_issue) { {} }
let(:importer) { importer_class.new(project, client) }
it 'returns true' do
expect(importer).to be_increment_object_counter(github_issue)
end
end
end

View File

@ -71,6 +71,65 @@ RSpec.describe Gitlab::Metrics::System do
SNIP
end
let(:mem_info) do
# full snapshot
<<~SNIP
MemTotal: 15362536 kB
MemFree: 3403136 kB
MemAvailable: 13044528 kB
Buffers: 272188 kB
Cached: 8171312 kB
SwapCached: 0 kB
Active: 3332084 kB
Inactive: 6981076 kB
Active(anon): 1603868 kB
Inactive(anon): 9044 kB
Active(file): 1728216 kB
Inactive(file): 6972032 kB
Unevictable: 18676 kB
Mlocked: 18676 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 6808 kB
Writeback: 0 kB
AnonPages: 1888300 kB
Mapped: 166164 kB
Shmem: 12932 kB
KReclaimable: 1275120 kB
Slab: 1495480 kB
SReclaimable: 1275120 kB
SUnreclaim: 220360 kB
KernelStack: 7072 kB
PageTables: 11936 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 7681268 kB
Committed_AS: 4976100 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 25532 kB
VmallocChunk: 0 kB
Percpu: 23200 kB
HardwareCorrupted: 0 kB
AnonHugePages: 202752 kB
ShmemHugePages: 0 kB
ShmemPmdMapped: 0 kB
FileHugePages: 0 kB
FilePmdMapped: 0 kB
CmaTotal: 0 kB
CmaFree: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 0 kB
DirectMap4k: 4637504 kB
DirectMap2M: 11087872 kB
DirectMap1G: 2097152 kB
SNIP
end
describe '.memory_usage_rss' do
context 'without PID' do
it "returns the current process' resident set size (RSS) in bytes" do
@ -125,6 +184,14 @@ RSpec.describe Gitlab::Metrics::System do
end
end
describe '.memory_total' do
it "returns the current process' resident set size (RSS) in bytes" do
mock_existing_proc_file('/proc/meminfo', mem_info)
expect(described_class.memory_total).to eq(15731236864)
end
end
describe '.process_runtime_elapsed_seconds' do
it 'returns the seconds elapsed since the process was started' do
# sets process starttime ticks to 1000

View File

@ -130,9 +130,10 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
end
it 'return true when everything is within limit', :aggregate_failures do
expect(memory_killer).to receive(:get_rss).and_return(100)
expect(memory_killer).to receive(:get_soft_limit_rss).and_return(200)
expect(memory_killer).to receive(:get_hard_limit_rss).and_return(300)
expect(memory_killer).to receive(:get_rss_kb).and_return(100)
expect(memory_killer).to receive(:get_soft_limit_rss_kb).and_return(200)
expect(memory_killer).to receive(:get_hard_limit_rss_kb).and_return(300)
expect(memory_killer).to receive(:get_memory_total_kb).and_return(3072)
expect(memory_killer).to receive(:refresh_state)
.with(:running)
@ -145,9 +146,10 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
end
it 'return false when rss exceeds hard_limit_rss', :aggregate_failures do
expect(memory_killer).to receive(:get_rss).at_least(:once).and_return(400)
expect(memory_killer).to receive(:get_soft_limit_rss).at_least(:once).and_return(200)
expect(memory_killer).to receive(:get_hard_limit_rss).at_least(:once).and_return(300)
expect(memory_killer).to receive(:get_rss_kb).at_least(:once).and_return(400)
expect(memory_killer).to receive(:get_soft_limit_rss_kb).at_least(:once).and_return(200)
expect(memory_killer).to receive(:get_hard_limit_rss_kb).at_least(:once).and_return(300)
expect(memory_killer).to receive(:get_memory_total_kb).at_least(:once).and_return(3072)
expect(memory_killer).to receive(:refresh_state)
.with(:running)
@ -165,9 +167,10 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
end
it 'return false when rss exceed hard_limit_rss after a while', :aggregate_failures do
expect(memory_killer).to receive(:get_rss).and_return(250, 400, 400)
expect(memory_killer).to receive(:get_soft_limit_rss).at_least(:once).and_return(200)
expect(memory_killer).to receive(:get_hard_limit_rss).at_least(:once).and_return(300)
expect(memory_killer).to receive(:get_rss_kb).and_return(250, 400, 400)
expect(memory_killer).to receive(:get_soft_limit_rss_kb).at_least(:once).and_return(200)
expect(memory_killer).to receive(:get_hard_limit_rss_kb).at_least(:once).and_return(300)
expect(memory_killer).to receive(:get_memory_total_kb).at_least(:once).and_return(3072)
expect(memory_killer).to receive(:refresh_state)
.with(:running)
@ -187,9 +190,10 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
end
it 'return true when rss below soft_limit_rss after a while within GRACE_BALLOON_SECONDS', :aggregate_failures do
expect(memory_killer).to receive(:get_rss).and_return(250, 100)
expect(memory_killer).to receive(:get_soft_limit_rss).and_return(200, 200)
expect(memory_killer).to receive(:get_hard_limit_rss).and_return(300, 300)
expect(memory_killer).to receive(:get_rss_kb).and_return(250, 100)
expect(memory_killer).to receive(:get_soft_limit_rss_kb).and_return(200, 200)
expect(memory_killer).to receive(:get_hard_limit_rss_kb).and_return(300, 300)
expect(memory_killer).to receive(:get_memory_total_kb).and_return(3072, 3072)
expect(memory_killer).to receive(:refresh_state)
.with(:running)
@ -211,9 +215,10 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
let(:grace_balloon_seconds) { 0 }
it 'return false when rss exceed soft_limit_rss', :aggregate_failures do
allow(memory_killer).to receive(:get_rss).and_return(250)
allow(memory_killer).to receive(:get_soft_limit_rss).and_return(200)
allow(memory_killer).to receive(:get_hard_limit_rss).and_return(300)
allow(memory_killer).to receive(:get_rss_kb).and_return(250)
allow(memory_killer).to receive(:get_soft_limit_rss_kb).and_return(200)
allow(memory_killer).to receive(:get_hard_limit_rss_kb).and_return(300)
allow(memory_killer).to receive(:get_memory_total_kb).and_return(3072)
expect(memory_killer).to receive(:refresh_state)
.with(:running)
@ -253,9 +258,10 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
stub_const("#{described_class}::SHUTDOWN_TIMEOUT_SECONDS", shutdown_timeout_seconds)
stub_feature_flags(sidekiq_memory_killer_read_only_mode: false)
allow(Sidekiq).to receive(:options).and_return(timeout: 9)
allow(memory_killer).to receive(:get_rss).and_return(100)
allow(memory_killer).to receive(:get_soft_limit_rss).and_return(200)
allow(memory_killer).to receive(:get_hard_limit_rss).and_return(300)
allow(memory_killer).to receive(:get_rss_kb).and_return(100)
allow(memory_killer).to receive(:get_soft_limit_rss_kb).and_return(200)
allow(memory_killer).to receive(:get_hard_limit_rss_kb).and_return(300)
allow(memory_killer).to receive(:get_memory_total_kb).and_return(3072)
end
it 'send signal' do
@ -367,6 +373,7 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
let(:current_rss) { 100 }
let(:soft_limit_rss) { 200 }
let(:hard_limit_rss) { 300 }
let(:memory_total) { 3072 }
let(:jid) { 1 }
let(:reason) { 'rss out of range reason description' }
let(:queue) { 'default' }
@ -385,9 +392,10 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
before do
stub_const("DummyWorker", worker)
allow(memory_killer).to receive(:get_rss).and_return(*current_rss)
allow(memory_killer).to receive(:get_soft_limit_rss).and_return(soft_limit_rss)
allow(memory_killer).to receive(:get_hard_limit_rss).and_return(hard_limit_rss)
allow(memory_killer).to receive(:get_rss_kb).and_return(*current_rss)
allow(memory_killer).to receive(:get_soft_limit_rss_kb).and_return(soft_limit_rss)
allow(memory_killer).to receive(:get_hard_limit_rss_kb).and_return(hard_limit_rss)
allow(memory_killer).to receive(:get_memory_total_kb).and_return(memory_total)
memory_killer.send(:refresh_state, :running)
end
@ -405,7 +413,8 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
hard_limit_rss: hard_limit_rss,
soft_limit_rss: soft_limit_rss,
reason: reason,
running_jobs: running_jobs)
running_jobs: running_jobs,
memory_total_kb: memory_total)
expect(metrics[:sidekiq_memory_killer_running_jobs]).to receive(:increment)
.with({ worker_class: "DummyWorker", deadline_exceeded: true })
@ -541,9 +550,10 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
subject { memory_killer.send(:refresh_state, :shutting_down) }
it 'calls gitlab metrics gauge set methods' do
expect(memory_killer).to receive(:get_rss) { 1010 }
expect(memory_killer).to receive(:get_soft_limit_rss) { 1020 }
expect(memory_killer).to receive(:get_hard_limit_rss) { 1040 }
expect(memory_killer).to receive(:get_rss_kb) { 1010 }
expect(memory_killer).to receive(:get_soft_limit_rss_kb) { 1020 }
expect(memory_killer).to receive(:get_hard_limit_rss_kb) { 1040 }
expect(memory_killer).to receive(:get_memory_total_kb) { 3072 }
expect(metrics[:sidekiq_memory_killer_phase]).to receive(:set)
.with({}, described_class::PHASE[:shutting_down])

View File

@ -45,30 +45,30 @@ RSpec.describe Sidebars::Projects::Menus::DeploymentsMenu do
it { is_expected.to be_nil }
end
end
describe 'when split_operations_visibility_permissions FF is disabled' do
shared_examples 'split_operations_visibility_permissions FF disabled' do
before do
stub_feature_flags(split_operations_visibility_permissions: false)
end
it { is_expected.not_to be_nil }
context 'and the feature is disabled' do
before do
stub_feature_flags(split_operations_visibility_permissions: false)
project.update_attribute("#{item_id}_access_level", 'disabled')
end
it { is_expected.not_to be_nil }
end
context 'and the feature is disabled' do
before do
project.update_attribute("#{item_id}_access_level", 'disabled')
end
it { is_expected.not_to be_nil }
context 'and operations is disabled' do
before do
project.update_attribute(:operations_access_level, 'disabled')
end
context 'and operations is disabled' do
before do
project.update_attribute(:operations_access_level, 'disabled')
end
it do
is_expected.to be_nil if [:environments, :feature_flags].include?(item_id)
end
it do
is_expected.to be_nil if [:environments, :feature_flags].include?(item_id)
end
end
end
@ -77,12 +77,14 @@ RSpec.describe Sidebars::Projects::Menus::DeploymentsMenu do
let(:item_id) { :feature_flags }
it_behaves_like 'access rights checks'
it_behaves_like 'split_operations_visibility_permissions FF disabled'
end
describe 'Environments' do
let(:item_id) { :environments }
it_behaves_like 'access rights checks'
it_behaves_like 'split_operations_visibility_permissions FF disabled'
end
describe 'Releases' do

View File

@ -3,15 +3,45 @@
require 'spec_helper'
RSpec.describe BulkImports::Failure, type: :model do
let(:failure) { create(:bulk_import_failure) }
describe 'associations' do
it { is_expected.to belong_to(:entity).required }
end
describe 'validations' do
before do
create(:bulk_import_failure)
end
it { is_expected.to validate_presence_of(:entity) }
end
describe '#relation' do
context 'when pipeline class is valid' do
it 'returns pipeline defined relation' do
failure.update!(pipeline_class: 'BulkImports::Common::Pipelines::WikiPipeline')
expect(failure.relation).to eq('wiki')
end
end
context 'when pipeline class is invalid' do
it 'returns default relation' do
failure.update!(pipeline_class: 'foobar')
expect(failure.relation).to eq('foobar')
end
context 'when pipeline class is outside of BulkImports namespace' do
it 'returns default relation' do
failure.update!(pipeline_class: 'Gitlab::ImportExport::Importer')
expect(failure.relation).to eq('importer')
end
end
it 'returns demodulized, underscored, chomped string' do
failure.update!(pipeline_class: 'BulkImports::Pipelines::Test::TestRelationPipeline')
expect(failure.relation).to eq('test_relation')
end
end
end
end

View File

@ -2817,6 +2817,14 @@ RSpec.describe Ci::Build do
end
end
context 'when the opt_in_jwt project setting is true' do
it 'does not include the JWT variables' do
project.ci_cd_settings.update!(opt_in_jwt: true)
expect(subject.pluck(:key)).not_to include('CI_JOB_JWT', 'CI_JOB_JWT_V1', 'CI_JOB_JWT_V2')
end
end
describe 'variables ordering' do
context 'when variables hierarchy is stubbed' do
let(:build_pre_var) { { key: 'build', value: 'value', public: true, masked: false } }
@ -3527,6 +3535,49 @@ RSpec.describe Ci::Build do
it { is_expected.to include(key: job_variable.key, value: job_variable.value, public: false, masked: false) }
end
context 'when ID tokens are defined on the build' do
before do
rsa_key = OpenSSL::PKey::RSA.generate(3072).to_s
stub_application_setting(ci_jwt_signing_key: rsa_key)
build.metadata.update!(id_tokens: {
'ID_TOKEN_1' => { id_token: { aud: 'developers' } },
'ID_TOKEN_2' => { id_token: { aud: 'maintainers' } }
})
end
subject(:runner_vars) { build.variables.to_runner_variables }
it 'includes the ID token variables' do
expect(runner_vars).to include(
a_hash_including(key: 'ID_TOKEN_1', public: false, masked: true),
a_hash_including(key: 'ID_TOKEN_2', public: false, masked: true)
)
id_token_var_1 = runner_vars.find { |var| var[:key] == 'ID_TOKEN_1' }
id_token_var_2 = runner_vars.find { |var| var[:key] == 'ID_TOKEN_2' }
id_token_1 = JWT.decode(id_token_var_1[:value], nil, false).first
id_token_2 = JWT.decode(id_token_var_2[:value], nil, false).first
expect(id_token_1['aud']).to eq('developers')
expect(id_token_2['aud']).to eq('maintainers')
end
context 'when a NoSigningKeyError is raised' do
it 'does not include the ID token variables' do
allow(::Gitlab::Ci::JwtV2).to receive(:for_build).and_raise(::Gitlab::Ci::Jwt::NoSigningKeyError)
expect(runner_vars.map { |var| var[:key] }).not_to include('ID_TOKEN_1', 'ID_TOKEN_2')
end
end
context 'when a RSAError is raised' do
it 'does not include the ID token variables' do
allow(::Gitlab::Ci::JwtV2).to receive(:for_build).and_raise(::OpenSSL::PKey::RSAError)
expect(runner_vars.map { |var| var[:key] }).not_to include('ID_TOKEN_1', 'ID_TOKEN_2')
end
end
end
end
describe '#scoped_variables' do

View File

@ -177,7 +177,7 @@ RSpec.describe Ci::Processable do
Ci::Build.attribute_names.map(&:to_sym) +
Ci::Build.attribute_aliases.keys.map(&:to_sym) +
Ci::Build.reflect_on_all_associations.map(&:name) +
[:tag_list, :needs_attributes, :job_variables_attributes] -
[:tag_list, :needs_attributes, :job_variables_attributes, :id_tokens] -
# ToDo: Move EE accessors to ee/
::Ci::Build.extra_accessors -
[:dast_site_profiles_build, :dast_scanner_profiles_build]

View File

@ -40,12 +40,20 @@ RSpec.describe Repository do
end
describe '#branch_names_contains' do
subject { repository.branch_names_contains(sample_commit.id) }
subject { repository.branch_names_contains(sample_commit.id, **opts) }
let(:opts) { {} }
it { is_expected.to include('master') }
it { is_expected.not_to include('feature') }
it { is_expected.not_to include('fix') }
context 'when limit is provided' do
let(:opts) { { limit: 1 } }
it { is_expected.to match_array(["'test'"]) }
end
describe 'when storage is broken', :broken_storage do
it 'raises a storage error' do
expect_to_raise_storage_error do
@ -56,10 +64,18 @@ RSpec.describe Repository do
end
describe '#tag_names_contains' do
subject { repository.tag_names_contains(sample_commit.id) }
subject { repository.tag_names_contains(sample_commit.id, **opts) }
let(:opts) { {} }
it { is_expected.to include('v1.1.0') }
it { is_expected.not_to include('v1.0.0') }
context 'when limit is provided' do
let(:opts) { { limit: 1 } }
it { is_expected.to match_array(['v1.1.0']) }
end
end
describe '#tags_sorted_by' do

View File

@ -602,6 +602,21 @@ RSpec.describe API::GenericPackages do
end
end
context 'with access to package registry for everyone' do
let_it_be(:user_role) { :anonymous }
before do
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
project.project_feature.update!(package_registry_access_level: ProjectFeature::PUBLIC)
end
it 'responds with success' do
download_file(auth_header)
expect(response).to have_gitlab_http_status(:success)
end
end
context 'with package status' do
where(:package_status, :expected_status) do
:default | :success

View File

@ -128,7 +128,6 @@ project_feature:
- infrastructure_access_level
- feature_flags_access_level
- environments_access_level
- releases_access_level
- project_id
- updated_at
computed_attributes:

View File

@ -221,6 +221,16 @@ RSpec.describe API::Projects do
expect(project_response['container_registry_enabled']).to eq(false)
end
it 'includes releases_access_level', :aggregate_failures do
project.project_feature.update!(releases_access_level: ProjectFeature::DISABLED)
get api('/projects', user)
project_response = json_response.find { |p| p['id'] == project.id }
expect(response).to have_gitlab_http_status(:ok)
expect(project_response['releases_access_level']).to eq('disabled')
end
context 'when some projects are in a group' do
before do
create(:project, :public, group: create(:group))
@ -1171,6 +1181,7 @@ RSpec.describe API::Projects do
attrs[:analytics_access_level] = 'disabled'
attrs[:container_registry_access_level] = 'private'
attrs[:security_and_compliance_access_level] = 'private'
attrs[:releases_access_level] = 'disabled'
end
post api('/projects', user), params: project
@ -1180,7 +1191,7 @@ RSpec.describe API::Projects do
project.each_pair do |k, v|
next if %i[
has_external_issue_tracker has_external_wiki issues_enabled merge_requests_enabled wiki_enabled storage_version
container_registry_access_level
container_registry_access_level releases_access_level
].include?(k)
expect(json_response[k.to_s]).to eq(v)
@ -1195,6 +1206,7 @@ RSpec.describe API::Projects do
expect(project.project_feature.analytics_access_level).to eq(ProjectFeature::DISABLED)
expect(project.project_feature.container_registry_access_level).to eq(ProjectFeature::PRIVATE)
expect(project.project_feature.security_and_compliance_access_level).to eq(ProjectFeature::PRIVATE)
expect(project.project_feature.releases_access_level).to eq(ProjectFeature::DISABLED)
end
it 'assigns container_registry_enabled to project', :aggregate_failures do
@ -2333,6 +2345,7 @@ RSpec.describe API::Projects do
expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to eq(project.only_allow_merge_if_all_discussions_are_resolved)
expect(json_response['operations_access_level']).to be_present
expect(json_response['security_and_compliance_access_level']).to be_present
expect(json_response['releases_access_level']).to be_present
end
it 'exposes all necessary attributes' do
@ -2402,6 +2415,7 @@ RSpec.describe API::Projects do
expect(json_response['builds_access_level']).to be_present
expect(json_response['operations_access_level']).to be_present
expect(json_response['security_and_compliance_access_level']).to be_present
expect(json_response['releases_access_level']).to be_present
expect(json_response).to have_key('emails_disabled')
expect(json_response['resolve_outdated_diff_discussions']).to eq(project.resolve_outdated_diff_discussions)
expect(json_response['remove_source_branch_after_merge']).to be_truthy
@ -3386,6 +3400,14 @@ RSpec.describe API::Projects do
expect(Project.find_by(path: project[:path]).analytics_access_level).to eq(ProjectFeature::PRIVATE)
end
it 'sets releases_access_level', :aggregate_failures do
put api("/projects/#{project.id}", user), params: { releases_access_level: 'private' }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['releases_access_level']).to eq('private')
expect(Project.find_by(path: project[:path]).releases_access_level).to eq(ProjectFeature::PRIVATE)
end
it 'returns 400 when nothing sent' do
project_param = {}

View File

@ -3,7 +3,10 @@ require 'spec_helper'
RSpec.describe Packages::Rpm::RepositoryMetadata::BaseBuilder do
describe '#execute' do
subject { described_class.new.execute }
subject { described_class.new(xml: xml, data: data).execute }
let(:xml) { nil }
let(:data) { {} }
before do
stub_const("#{described_class}::ROOT_TAG", 'test')
@ -18,5 +21,13 @@ RSpec.describe Packages::Rpm::RepositoryMetadata::BaseBuilder do
expect(result.children.first.attributes['foo1'].value).to eq('bar1')
expect(result.children.first.attributes['foo2'].value).to eq('bar2')
end
context 'when call with parameters' do
let(:xml) { 'test' }
it 'raise NotImplementedError' do
expect { subject }.to raise_error NotImplementedError
end
end
end
end

View File

@ -3,18 +3,32 @@ require 'spec_helper'
RSpec.describe Packages::Rpm::RepositoryMetadata::BuildPrimaryXml do
describe '#execute' do
subject { described_class.new.execute }
subject { described_class.new(xml: xml, data: data).execute }
context "when generate empty xml" do
let(:expected_xml) do
<<~XML
<?xml version="1.0" encoding="UTF-8"?>
<metadata xmlns="http://linux.duke.edu/metadata/common" xmlns:rpm="http://linux.duke.edu/metadata/rpm" packages="0"/>
XML
end
let(:empty_xml) do
<<~XML
<?xml version="1.0" encoding="UTF-8"?>
<metadata xmlns="http://linux.duke.edu/metadata/common" xmlns:rpm="http://linux.duke.edu/metadata/rpm" packages="0"/>
XML
end
it 'generate expected xml' do
expect(subject).to eq(expected_xml)
it_behaves_like 'handling rpm xml file'
context 'when updating existing xml' do
include_context 'with rpm package data'
let(:xml) { empty_xml }
let(:data) { xml_update_params }
let(:required_text_only_attributes) { %i[description summary arch name] }
it 'adds node with required_text_only_attributes' do
result = Nokogiri::XML::Document.parse(subject).remove_namespaces!
required_text_only_attributes.each do |attribute|
expect(
result.at("//#{described_class::ROOT_TAG}/package/#{attribute}").text
).to eq(data[attribute])
end
end
end
end

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
RSpec.shared_context 'with rpm package data' do
def xml_update_params
Gitlab::Json.parse(fixture_file('packages/rpm/payload.json')).with_indifferent_access
end
end

View File

@ -0,0 +1,52 @@
# frozen_string_literal: true
RSpec.shared_examples 'handling rpm xml file' do
include_context 'with rpm package data'
let(:xml) { nil }
let(:data) { {} }
context 'when generate empty xml' do
it 'generate expected xml' do
expect(subject).to eq(empty_xml)
end
end
context 'when updating existing xml' do
let(:xml) { empty_xml }
let(:data) { xml_update_params }
shared_examples 'changing root tag attribute' do
it "increment previous 'packages' value by 1" do
previous_value = Nokogiri::XML(xml).at(described_class::ROOT_TAG).attributes["packages"].value.to_i
new_value = Nokogiri::XML(subject).at(described_class::ROOT_TAG).attributes["packages"].value.to_i
expect(previous_value + 1).to eq(new_value)
end
end
it 'generate valid xml add expected xml node to existing xml' do
# Have one root attribute
result = Nokogiri::XML::Document.parse(subject).remove_namespaces!
expect(result.children.count).to eq(1)
# Root node has 1 child with generated node
expect(result.xpath("//#{described_class::ROOT_TAG}/package").count).to eq(1)
end
context 'when empty xml' do
it_behaves_like 'changing root tag attribute'
end
context 'when xml has children' do
let(:xml) { described_class.new(xml: empty_xml, data: data).execute }
it 'has children nodes' do
result = Nokogiri::XML::Document.parse(xml).remove_namespaces!
expect(result.children.count).to be > 0
end
it_behaves_like 'changing root tag attribute'
end
end
end

View File

@ -376,7 +376,7 @@ RSpec.describe BulkImports::PipelineWorker do
hash_including(
'pipeline_name' => 'NdjsonPipeline',
'entity_id' => entity.id,
'message' => 'Error!'
'message' => 'Export from source instance failed: Error!'
)
)
end

View File

@ -195,4 +195,12 @@ RSpec.describe Gitlab::GithubImport::ObjectImporter, :aggregate_failures do
end
end
end
describe '#increment_object_counter?' do
let(:issue) { double(:issue, pull_request?: true) }
it 'returns true' do
expect(worker).to be_increment_object_counter(issue)
end
end
end

View File

@ -45,4 +45,15 @@ RSpec.describe Gitlab::GithubImport::ImportIssueWorker do
worker.import(project, client, hash)
end
end
describe '#increment_object_counter?' do
context 'when github issue is a pull request' do
let(:issue) { double(:issue, pull_request?: true) }
let(:project) { double(:project) }
it 'returns false' do
expect(worker).not_to be_increment_object_counter(issue)
end
end
end
end