Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/lib/utils/text_markdown.js2
-rw-r--r--app/assets/stylesheets/pages/commits.scss4
-rw-r--r--app/controllers/admin/background_migrations_controller.rb9
-rw-r--r--app/models/clusters/agent.rb3
-rw-r--r--app/models/clusters/agents/group_authorization.rb14
-rw-r--r--app/models/internal_id.rb116
-rw-r--r--app/models/project.rb4
-rw-r--r--app/services/clusters/agents/refresh_authorization_service.rb59
-rw-r--r--app/services/issues/update_service.rb49
-rw-r--r--app/validators/json_schemas/cluster_agent_authorization_configuration.json6
-rw-r--r--app/views/admin/background_migrations/_migration.html.haml4
-rw-r--r--config/feature_flags/development/use_insert_all_in_internal_id.yml8
-rw-r--r--config/routes/admin.rb1
-rw-r--r--db/migrate/20210809014850_create_agent_group_authorizations.rb16
-rw-r--r--db/migrate/20210809014918_add_agent_group_authorizations_foreign_keys.rb22
-rw-r--r--db/migrate/20210818220234_add_default_project_approval_rules_vuln_allowed.rb24
-rw-r--r--db/schema_migrations/202108090148501
-rw-r--r--db/schema_migrations/202108090149181
-rw-r--r--db/schema_migrations/202108182202341
-rw-r--r--db/structure.sql33
-rw-r--r--doc/ci/variables/predefined_variables.md3
-rw-r--r--doc/user/packages/dependency_proxy/index.md12
-rw-r--r--lib/api/internal/kubernetes.rb17
-rw-r--r--lib/gitlab/database/background_migration/batched_job.rb1
-rw-r--r--lib/gitlab/database/background_migration/batched_migration.rb11
-rw-r--r--lib/sidebars/groups/menus/ci_cd_menu.rb5
-rw-r--r--lib/sidebars/groups/menus/group_information_menu.rb5
-rw-r--r--lib/sidebars/groups/menus/issues_menu.rb5
-rw-r--r--lib/sidebars/groups/menus/packages_registries_menu.rb5
-rw-r--r--lib/sidebars/groups/menus/settings_menu.rb5
-rw-r--r--lib/sidebars/menu.rb3
-rw-r--r--lib/sidebars/projects/menus/analytics_menu.rb2
-rw-r--r--lib/sidebars/projects/menus/ci_cd_menu.rb5
-rw-r--r--lib/sidebars/projects/menus/deployments_menu.rb5
-rw-r--r--lib/sidebars/projects/menus/infrastructure_menu.rb5
-rw-r--r--lib/sidebars/projects/menus/issues_menu.rb5
-rw-r--r--lib/sidebars/projects/menus/monitor_menu.rb5
-rw-r--r--lib/sidebars/projects/menus/packages_registries_menu.rb5
-rw-r--r--lib/sidebars/projects/menus/project_information_menu.rb5
-rw-r--r--lib/sidebars/projects/menus/repository_menu.rb5
-rw-r--r--lib/sidebars/projects/menus/security_compliance_menu.rb5
-rw-r--r--lib/sidebars/projects/menus/settings_menu.rb5
-rw-r--r--spec/factories/clusters/agents/group_authorizations.rb10
-rw-r--r--spec/features/admin/admin_sees_background_migrations_spec.rb39
-rw-r--r--spec/frontend/lib/utils/text_markdown_spec.js19
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_spec.rb36
-rw-r--r--spec/lib/sidebars/menu_spec.rb23
-rw-r--r--spec/lib/sidebars/projects/menus/monitor_menu_spec.rb19
-rw-r--r--spec/migrations/add_default_project_approval_rules_vuln_allowed_spec.rb35
-rw-r--r--spec/models/ci/build_spec.rb4
-rw-r--r--spec/models/clusters/agent_spec.rb2
-rw-r--r--spec/models/clusters/agents/group_authorization_spec.rb10
-rw-r--r--spec/models/internal_id_spec.rb44
-rw-r--r--spec/requests/admin/background_migrations_controller_spec.rb45
-rw-r--r--spec/requests/api/internal/kubernetes_spec.rb38
-rw-r--r--spec/services/clusters/agents/refresh_authorization_service_spec.rb84
57 files changed, 715 insertions, 196 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 66f8e86e48d..c742253cb00 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-48d7984d9912c935a2c2abba3b55593cf0be2d8e
+de019fc19eeb8bc6a65a6dbd8bf236669c777815
diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js
index 1641da3e009..6ff2af47dd8 100644
--- a/app/assets/javascripts/lib/utils/text_markdown.js
+++ b/app/assets/javascripts/lib/utils/text_markdown.js
@@ -232,7 +232,7 @@ export function insertMarkdownText({
.join('\n');
}
} else if (tag.indexOf(textPlaceholder) > -1) {
- textToInsert = tag.replace(textPlaceholder, () => selected);
+ textToInsert = tag.replace(textPlaceholder, () => selected.replace(/\\n/g, '\n'));
} else {
textToInsert = String(startChar) + tag + selected + (wrap ? tag : '');
}
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 5173aeb824e..bc4dbf695cf 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -250,6 +250,10 @@
.commit-row-description {
display: none;
flex: 1;
+
+ a {
+ color: $blue-600;
+ }
}
&.inline-commit {
diff --git a/app/controllers/admin/background_migrations_controller.rb b/app/controllers/admin/background_migrations_controller.rb
index 65b47308e4c..e21e6fd2dcb 100644
--- a/app/controllers/admin/background_migrations_controller.rb
+++ b/app/controllers/admin/background_migrations_controller.rb
@@ -29,9 +29,16 @@ class Admin::BackgroundMigrationsController < Admin::ApplicationController
redirect_back fallback_location: { action: 'index' }
end
+ def retry
+ migration = batched_migration_class.find(params[:id])
+ migration.retry_failed_jobs! if migration.failed?
+
+ redirect_back fallback_location: { action: 'index' }
+ end
+
private
def batched_migration_class
- Gitlab::Database::BackgroundMigration::BatchedMigration
+ @batched_migration_class ||= Gitlab::Database::BackgroundMigration::BatchedMigration
end
end
diff --git a/app/models/clusters/agent.rb b/app/models/clusters/agent.rb
index 9fb8cd024c5..8c19a691144 100644
--- a/app/models/clusters/agent.rb
+++ b/app/models/clusters/agent.rb
@@ -10,6 +10,9 @@ module Clusters
has_many :agent_tokens, class_name: 'Clusters::AgentToken'
has_many :last_used_agent_tokens, -> { order_last_used_at_desc }, class_name: 'Clusters::AgentToken', inverse_of: :agent
+ has_many :group_authorizations, class_name: 'Clusters::Agents::GroupAuthorization'
+ has_many :authorized_groups, class_name: '::Group', through: :group_authorizations, source: :group
+
scope :ordered_by_name, -> { order(:name) }
scope :with_name, -> (name) { where(name: name) }
diff --git a/app/models/clusters/agents/group_authorization.rb b/app/models/clusters/agents/group_authorization.rb
new file mode 100644
index 00000000000..96a9997be75
--- /dev/null
+++ b/app/models/clusters/agents/group_authorization.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Clusters
+ module Agents
+ class GroupAuthorization < ApplicationRecord
+ self.table_name = 'agent_group_authorizations'
+
+ belongs_to :agent, class_name: 'Clusters::Agent', optional: false
+ belongs_to :group, class_name: '::Group', optional: false
+
+ validates :config, json_schema: { filename: 'cluster_agent_authorization_configuration' }
+ end
+ end
+end
diff --git a/app/models/internal_id.rb b/app/models/internal_id.rb
index a54de3c82d1..107b1914af4 100644
--- a/app/models/internal_id.rb
+++ b/app/models/internal_id.rb
@@ -201,30 +201,55 @@ class InternalId < ApplicationRecord
InternalId.find_by(**scope, usage: usage_value)
end
+ def initial_value(subject, scope)
+ raise ArgumentError, 'Cannot initialize without init!' unless init
+
+ # `init` computes the maximum based on actual records. We use the
+ # primary to make sure we have up to date results
+ Gitlab::Database::LoadBalancing::Session.current.use_primary do
+ instance = subject.is_a?(::Class) ? nil : subject
+
+ init.call(instance, scope) || 0
+ end
+ end
+
def usage_value
@usage_value ||= InternalId.usages[usage.to_s]
end
# Create InternalId record for (scope, usage) combination, if it doesn't exist
#
- # We blindly insert without synchronization. If another process
- # was faster in doing this, we'll realize once we hit the unique key constraint
- # violation. We can safely roll-back the nested transaction and perform
- # a lookup instead to retrieve the record.
+ # We blindly insert ignoring conflicts on the unique key constraint.
+ # If another process was faster in doing this, we'll end up with that record
+ # when we do the lookup after the insert.
def create_record
- raise ArgumentError, 'Cannot initialize without init!' unless init
-
- instance = subject.is_a?(::Class) ? nil : subject
+ if Feature.enabled?(:use_insert_all_in_internal_id, default_enabled: :yaml)
+ scope[:project].save! if scope[:project] && !scope[:project].persisted?
+ scope[:namespace].save! if scope[:namespace] && !scope[:namespace].persisted?
- subject.transaction(requires_new: true) do
- InternalId.create!(
- **scope,
+ attributes = {
+ project_id: scope[:project]&.id || scope[:project_id],
+ namespace_id: scope[:namespace]&.id || scope[:namespace_id],
usage: usage_value,
- last_value: init.call(instance, scope) || 0
- )
+ last_value: initial_value(subject, scope)
+ }
+
+ InternalId.insert_all([attributes])
+
+ lookup
+ else
+ begin
+ subject.transaction(requires_new: true) do
+ InternalId.create!(
+ **scope,
+ usage: usage_value,
+ last_value: initial_value(subject, scope)
+ )
+ end
+ rescue ActiveRecord::RecordNotUnique
+ lookup
+ end
end
- rescue ActiveRecord::RecordNotUnique
- lookup
end
end
@@ -247,6 +272,8 @@ class InternalId < ApplicationRecord
# init: Proc that accepts the subject and the scope and returns Integer|NilClass
attr_reader :subject, :scope, :scope_attrs, :usage, :init
+ RecordAlreadyExists = Class.new(StandardError)
+
def initialize(subject, scope, usage, init = nil)
@subject = subject
@scope = scope
@@ -270,10 +297,8 @@ class InternalId < ApplicationRecord
return next_iid if next_iid
- create_record!(subject, scope, usage, init) do |iid|
- iid.last_value += 1
- end
- rescue ActiveRecord::RecordNotUnique
+ create_record!(subject, scope, usage, initial_value(subject, scope) + 1)
+ rescue RecordAlreadyExists
retry
end
@@ -302,10 +327,8 @@ class InternalId < ApplicationRecord
next_iid = update_record!(subject, scope, usage, function)
return next_iid if next_iid
- create_record!(subject, scope, usage, init) do |object|
- object.last_value = [object.last_value, new_value].max
- end
- rescue ActiveRecord::RecordNotUnique
+ create_record!(subject, scope, usage, [initial_value(subject, scope), new_value].max)
+ rescue RecordAlreadyExists
retry
end
@@ -317,27 +340,56 @@ class InternalId < ApplicationRecord
stmt.set(arel_table[:last_value] => new_value)
stmt.wheres = InternalId.filter_by(scope, usage).arel.constraints
- ActiveRecord::Base.connection.insert(stmt, 'Update InternalId', 'last_value') # rubocop: disable Database/MultipleDatabases
+ InternalId.connection.insert(stmt, 'Update InternalId', 'last_value')
end
- def create_record!(subject, scope, usage, init)
- raise ArgumentError, 'Cannot initialize without init!' unless init
+ def create_record!(subject, scope, usage, value)
+ if Feature.enabled?(:use_insert_all_in_internal_id, default_enabled: :yaml)
+ scope[:project].save! if scope[:project] && !scope[:project].persisted?
+ scope[:namespace].save! if scope[:namespace] && !scope[:namespace].persisted?
- instance = subject.is_a?(::Class) ? nil : subject
+ attributes = {
+ project_id: scope[:project]&.id || scope[:project_id],
+ namespace_id: scope[:namespace]&.id || scope[:namespace_id],
+ usage: usage_value,
+ last_value: value
+ }
- subject.transaction(requires_new: true) do
- last_value = init.call(instance, scope) || 0
+ result = InternalId.insert_all([attributes])
- internal_id = InternalId.create!(**scope, usage: usage, last_value: last_value) do |subject|
- yield subject if block_given?
- end
+ raise RecordAlreadyExists if result.empty?
- internal_id.last_value
+ value
+ else
+ begin
+ subject.transaction(requires_new: true) do
+ internal_id = InternalId.create!(**scope, usage: usage, last_value: value)
+ internal_id.last_value
+ end
+ rescue ActiveRecord::RecordNotUnique
+ raise RecordAlreadyExists
+ end
end
end
def arel_table
InternalId.arel_table
end
+
+ def initial_value(subject, scope)
+ raise ArgumentError, 'Cannot initialize without init!' unless init
+
+ # `init` computes the maximum based on actual records. We use the
+ # primary to make sure we have up to date results
+ Gitlab::Database::LoadBalancing::Session.current.use_primary do
+ instance = subject.is_a?(::Class) ? nil : subject
+
+ init.call(instance, scope) || 0
+ end
+ end
+
+ def usage_value
+ @usage_value ||= InternalId.usages[usage.to_s]
+ end
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 81b04e1316c..5804741f6b4 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -2093,6 +2093,10 @@ class Project < ApplicationRecord
# Docker doesn't allow. The proxy expects it to be downcased.
value: "#{Gitlab.host_with_port}/#{namespace.root_ancestor.path.downcase}#{DependencyProxy::URL_SUFFIX}"
)
+ variables.append(
+ key: 'CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX',
+ value: "#{Gitlab.host_with_port}/#{namespace.full_path.downcase}#{DependencyProxy::URL_SUFFIX}"
+ )
end
end
diff --git a/app/services/clusters/agents/refresh_authorization_service.rb b/app/services/clusters/agents/refresh_authorization_service.rb
new file mode 100644
index 00000000000..0da012da861
--- /dev/null
+++ b/app/services/clusters/agents/refresh_authorization_service.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+module Clusters
+ module Agents
+ class RefreshAuthorizationService
+ include Gitlab::Utils::StrongMemoize
+
+ AUTHORIZED_GROUP_LIMIT = 100
+
+ delegate :project, to: :agent, private: true
+
+ def initialize(agent, config:)
+ @agent = agent
+ @config = config
+ end
+
+ def execute
+ if allowed_group_configurations.present?
+ group_ids = allowed_group_configurations.map { |config| config.fetch(:group_id) }
+
+ agent.with_lock do
+ agent.group_authorizations.upsert_all(allowed_group_configurations, unique_by: [:agent_id, :group_id])
+ agent.group_authorizations.where.not(group_id: group_ids).delete_all # rubocop: disable CodeReuse/ActiveRecord
+ end
+ else
+ agent.group_authorizations.delete_all(:delete_all)
+ end
+
+ true
+ end
+
+ private
+
+ attr_reader :agent, :config
+
+ def allowed_group_configurations
+ strong_memoize(:allowed_group_configurations) do
+ group_entries = config.dig('ci_access', 'groups')&.first(AUTHORIZED_GROUP_LIMIT)
+
+ if group_entries
+ groups_by_path = group_entries.index_by { |config| config.delete('id') }
+
+ allowed_groups.where_full_path_in(groups_by_path.keys).map do |group|
+ { group_id: group.id, config: groups_by_path[group.full_path] }
+ end
+ end
+ end
+ end
+
+ def allowed_groups
+ if project.root_ancestor.group?
+ project.root_ancestor.self_and_descendants
+ else
+ ::Group.none
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 4e52acfcdda..6ee94e014d8 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -54,28 +54,10 @@ module Issues
end
handle_assignee_changes(issue, old_assignees)
-
- if issue.previous_changes.include?('confidential')
- # 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)
- end
-
- added_labels = issue.labels - old_labels
-
- if added_labels.present?
- notification_service.async.relabeled_issue(issue, added_labels, current_user)
- end
-
+ handle_confidential_change(issue)
+ handle_added_labels(issue, old_labels)
handle_milestone_change(issue)
-
- added_mentions = issue.mentioned_users(current_user) - old_mentioned_users
-
- if added_mentions.present?
- notification_service.async.new_mentions_in_issue(issue, added_mentions, current_user)
- end
-
+ handle_added_mentions(issue, old_mentioned_users)
handle_severity_change(issue, old_severity)
handle_issue_type_change(issue)
end
@@ -157,6 +139,23 @@ module Issues
MergeRequests::CreateFromIssueService.new(project: project, current_user: current_user, mr_params: create_merge_request_params).execute
end
+ def handle_confidential_change(issue)
+ if issue.previous_changes.include?('confidential')
+ # 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)
+ end
+ end
+
+ def handle_added_labels(issue, old_labels)
+ added_labels = issue.labels - old_labels
+
+ if added_labels.present?
+ notification_service.async.relabeled_issue(issue, added_labels, current_user)
+ end
+ end
+
def handle_milestone_change(issue)
return unless issue.previous_changes.include?('milestone_id')
@@ -185,6 +184,14 @@ module Issues
end
end
+ def handle_added_mentions(issue, old_mentioned_users)
+ added_mentions = issue.mentioned_users(current_user) - old_mentioned_users
+
+ if added_mentions.present?
+ notification_service.async.new_mentions_in_issue(issue, added_mentions, current_user)
+ end
+ end
+
def handle_severity_change(issue, old_severity)
return unless old_severity && issue.severity != old_severity
diff --git a/app/validators/json_schemas/cluster_agent_authorization_configuration.json b/app/validators/json_schemas/cluster_agent_authorization_configuration.json
new file mode 100644
index 00000000000..f3de0b7043b
--- /dev/null
+++ b/app/validators/json_schemas/cluster_agent_authorization_configuration.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "description": "Cluster Agent configuration for an authorized project or group",
+ "type": "object",
+ "additionalProperties": true
+}
diff --git a/app/views/admin/background_migrations/_migration.html.haml b/app/views/admin/background_migrations/_migration.html.haml
index ddb2eb27705..4a7c0083bc7 100644
--- a/app/views/admin/background_migrations/_migration.html.haml
+++ b/app/views/admin/background_migrations/_migration.html.haml
@@ -17,3 +17,7 @@
= button_to resume_admin_background_migration_path(migration),
class: 'gl-button btn btn-icon has-tooltip', title: _('Resume'), 'aria-label' => _('Resume') do
= sprite_icon('play', css_class: 'gl-button-icon gl-icon')
+ - elsif migration.failed?
+ = button_to retry_admin_background_migration_path(migration),
+ class: 'gl-button btn btn-icon has-tooltip', title: _('Retry'), 'aria-label' => _('Retry') do
+ = sprite_icon('retry', css_class: 'gl-button-icon gl-icon')
diff --git a/config/feature_flags/development/use_insert_all_in_internal_id.yml b/config/feature_flags/development/use_insert_all_in_internal_id.yml
new file mode 100644
index 00000000000..3529a6769a3
--- /dev/null
+++ b/config/feature_flags/development/use_insert_all_in_internal_id.yml
@@ -0,0 +1,8 @@
+---
+name: use_insert_all_in_internal_id
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68617
+rollout_issue_url:
+milestone: '14.3'
+type: development
+group: group::project management
+default_enabled: false
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index d7f73354d4c..e3b365ad276 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -93,6 +93,7 @@ namespace :admin do
member do
post :pause
post :resume
+ post :retry
end
end
diff --git a/db/migrate/20210809014850_create_agent_group_authorizations.rb b/db/migrate/20210809014850_create_agent_group_authorizations.rb
new file mode 100644
index 00000000000..43d7e63e0a2
--- /dev/null
+++ b/db/migrate/20210809014850_create_agent_group_authorizations.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class CreateAgentGroupAuthorizations < ActiveRecord::Migration[6.1]
+ include Gitlab::Database::MigrationHelpers
+
+ def change
+ create_table :agent_group_authorizations do |t|
+ t.bigint :group_id, null: false
+ t.bigint :agent_id, null: false
+ t.jsonb :config, null: false
+
+ t.index :group_id
+ t.index [:agent_id, :group_id], unique: true
+ end
+ end
+end
diff --git a/db/migrate/20210809014918_add_agent_group_authorizations_foreign_keys.rb b/db/migrate/20210809014918_add_agent_group_authorizations_foreign_keys.rb
new file mode 100644
index 00000000000..2a3a51d0ca9
--- /dev/null
+++ b/db/migrate/20210809014918_add_agent_group_authorizations_foreign_keys.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class AddAgentGroupAuthorizationsForeignKeys < ActiveRecord::Migration[6.1]
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :agent_group_authorizations, :namespaces, column: :group_id
+ add_concurrent_foreign_key :agent_group_authorizations, :cluster_agents, column: :agent_id
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key_if_exists :agent_group_authorizations, column: :group_id
+ end
+
+ with_lock_retries do
+ remove_foreign_key_if_exists :agent_group_authorizations, column: :agent_id
+ end
+ end
+end
diff --git a/db/migrate/20210818220234_add_default_project_approval_rules_vuln_allowed.rb b/db/migrate/20210818220234_add_default_project_approval_rules_vuln_allowed.rb
new file mode 100644
index 00000000000..72d2755effa
--- /dev/null
+++ b/db/migrate/20210818220234_add_default_project_approval_rules_vuln_allowed.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class AddDefaultProjectApprovalRulesVulnAllowed < ActiveRecord::Migration[6.1]
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ DEFAULT_VALUE = 0
+
+ def up
+ change_column_default :approval_project_rules, :vulnerabilities_allowed, DEFAULT_VALUE
+
+ update_column_in_batches(:approval_project_rules, :vulnerabilities_allowed, DEFAULT_VALUE) do |table, query|
+ query.where(table[:vulnerabilities_allowed].eq(nil))
+ end
+
+ change_column_null :approval_project_rules, :vulnerabilities_allowed, false
+ end
+
+ def down
+ change_column_default :approval_project_rules, :vulnerabilities_allowed, nil
+ change_column_null :approval_project_rules, :vulnerabilities_allowed, true
+ end
+end
diff --git a/db/schema_migrations/20210809014850 b/db/schema_migrations/20210809014850
new file mode 100644
index 00000000000..541d397d169
--- /dev/null
+++ b/db/schema_migrations/20210809014850
@@ -0,0 +1 @@
+6f67e2bba5f42d48a9b21f8ab4d9abf4495ef7e0226ea903d51e77eed85ad0cb \ No newline at end of file
diff --git a/db/schema_migrations/20210809014918 b/db/schema_migrations/20210809014918
new file mode 100644
index 00000000000..099f032c76a
--- /dev/null
+++ b/db/schema_migrations/20210809014918
@@ -0,0 +1 @@
+d282a027d03920a53d49444f54745ab7d2c8bcccc485ac9407ff9dbbef77981f \ No newline at end of file
diff --git a/db/schema_migrations/20210818220234 b/db/schema_migrations/20210818220234
new file mode 100644
index 00000000000..e32f27029cf
--- /dev/null
+++ b/db/schema_migrations/20210818220234
@@ -0,0 +1 @@
+8d247218468ad383d1a8a2dc67d5e7e67ddad2a33a38203a41e49c4c018adc7e \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 3c06ee0977e..860b2d6f287 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -8958,6 +8958,22 @@ CREATE SEQUENCE abuse_reports_id_seq
ALTER SEQUENCE abuse_reports_id_seq OWNED BY abuse_reports.id;
+CREATE TABLE agent_group_authorizations (
+ id bigint NOT NULL,
+ group_id bigint NOT NULL,
+ agent_id bigint NOT NULL,
+ config jsonb NOT NULL
+);
+
+CREATE SEQUENCE agent_group_authorizations_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE agent_group_authorizations_id_seq OWNED BY agent_group_authorizations.id;
+
CREATE TABLE alert_management_alert_assignees (
id bigint NOT NULL,
user_id bigint NOT NULL,
@@ -9746,7 +9762,7 @@ CREATE TABLE approval_project_rules (
name character varying NOT NULL,
rule_type smallint DEFAULT 0 NOT NULL,
scanners text[],
- vulnerabilities_allowed smallint,
+ vulnerabilities_allowed smallint DEFAULT 0 NOT NULL,
severity_levels text[] DEFAULT '{}'::text[] NOT NULL
);
@@ -20066,6 +20082,8 @@ ALTER SEQUENCE zoom_meetings_id_seq OWNED BY zoom_meetings.id;
ALTER TABLE ONLY abuse_reports ALTER COLUMN id SET DEFAULT nextval('abuse_reports_id_seq'::regclass);
+ALTER TABLE ONLY agent_group_authorizations ALTER COLUMN id SET DEFAULT nextval('agent_group_authorizations_id_seq'::regclass);
+
ALTER TABLE ONLY alert_management_alert_assignees ALTER COLUMN id SET DEFAULT nextval('alert_management_alert_assignees_id_seq'::regclass);
ALTER TABLE ONLY alert_management_alert_user_mentions ALTER COLUMN id SET DEFAULT nextval('alert_management_alert_user_mentions_id_seq'::regclass);
@@ -21190,6 +21208,9 @@ ALTER TABLE ONLY gitlab_partitions_static.product_analytics_events_experimental_
ALTER TABLE ONLY abuse_reports
ADD CONSTRAINT abuse_reports_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY agent_group_authorizations
+ ADD CONSTRAINT agent_group_authorizations_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY alert_management_alert_assignees
ADD CONSTRAINT alert_management_alert_assignees_pkey PRIMARY KEY (id);
@@ -23113,6 +23134,10 @@ CREATE UNIQUE INDEX idx_vulnerability_issue_links_on_vulnerability_id_and_link_t
CREATE INDEX index_abuse_reports_on_user_id ON abuse_reports USING btree (user_id);
+CREATE UNIQUE INDEX index_agent_group_authorizations_on_agent_id_and_group_id ON agent_group_authorizations USING btree (agent_id, group_id);
+
+CREATE INDEX index_agent_group_authorizations_on_group_id ON agent_group_authorizations USING btree (group_id);
+
CREATE INDEX index_alert_assignees_on_alert_id ON alert_management_alert_assignees USING btree (alert_id);
CREATE UNIQUE INDEX index_alert_assignees_on_user_id_and_alert_id ON alert_management_alert_assignees USING btree (user_id, alert_id);
@@ -26293,6 +26318,9 @@ ALTER TABLE ONLY geo_event_log
ALTER TABLE ONLY deployments
ADD CONSTRAINT fk_289bba3222 FOREIGN KEY (cluster_id) REFERENCES clusters(id) ON DELETE SET NULL;
+ALTER TABLE ONLY agent_group_authorizations
+ ADD CONSTRAINT fk_2c9f941965 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY ci_freeze_periods
ADD CONSTRAINT fk_2e02bbd1a6 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
@@ -26953,6 +26981,9 @@ ALTER TABLE ONLY dep_ci_build_trace_section_names
ALTER TABLE ONLY ci_stages
ADD CONSTRAINT fk_fb57e6cc56 FOREIGN KEY (pipeline_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
+ALTER TABLE ONLY agent_group_authorizations
+ ADD CONSTRAINT fk_fb70782616 FOREIGN KEY (agent_id) REFERENCES cluster_agents(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY system_note_metadata
ADD CONSTRAINT fk_fbd87415c9 FOREIGN KEY (description_version_id) REFERENCES description_versions(id) ON DELETE SET NULL;
diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md
index b5999a555c9..ab9a548eb6e 100644
--- a/doc/ci/variables/predefined_variables.md
+++ b/doc/ci/variables/predefined_variables.md
@@ -41,7 +41,8 @@ There are also [Kubernetes-specific deployment variables](../../user/project/clu
| `CI_CONFIG_PATH` | 9.4 | 0.5 | The path to the CI/CD configuration file. Defaults to `.gitlab-ci.yml`. Read-only inside a running pipeline. |
| `CI_DEBUG_TRACE` | all | 1.7 | `true` if [debug logging (tracing)](index.md#debug-logging) is enabled. |
| `CI_DEFAULT_BRANCH` | 12.4 | all | The name of the project's default branch. |
-| `CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX` | 13.7 | all | The image prefix for pulling images through the Dependency Proxy. |
+| `CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX` | 13.7 | all | The top-level group image prefix for pulling images through the Dependency Proxy. |
+| `CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX` | 14.3 | all | The direct group image prefix for pulling images through the Dependency Proxy. |
| `CI_DEPENDENCY_PROXY_PASSWORD` | 13.7 | all | The password to pull images through the Dependency Proxy. |
| `CI_DEPENDENCY_PROXY_SERVER` | 13.7 | all | The server for logging in to the Dependency Proxy. This is equivalent to `$CI_SERVER_HOST:$CI_SERVER_PORT`. |
| `CI_DEPENDENCY_PROXY_USER` | 13.7 | all | The username to pull images through the Dependency Proxy. |
diff --git a/doc/user/packages/dependency_proxy/index.md b/doc/user/packages/dependency_proxy/index.md
index c76b0a6810f..ad25ec7edbf 100644
--- a/doc/user/packages/dependency_proxy/index.md
+++ b/doc/user/packages/dependency_proxy/index.md
@@ -94,8 +94,11 @@ Proxy.
> - The prefix for group names containing uppercase letters was [fixed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54559) in GitLab 13.10.
Runners log in to the Dependency Proxy automatically. To pull through
-the Dependency Proxy, use the `CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX`
-[predefined CI/CD variable](../../../ci/variables/predefined_variables.md):
+the Dependency Proxy, use one of the [predefined variables](../../../ci/variables/predefined_variables.md):
+
+- `CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX` pulls through the top-level group.
+- `CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX` pulls through the subgroup, or direct group the
+ project exists in.
Example pulling the latest alpine image:
@@ -109,7 +112,10 @@ There are other additional predefined CI/CD variables you can also use:
- `CI_DEPENDENCY_PROXY_USER`: A CI/CD user for logging in to the Dependency Proxy.
- `CI_DEPENDENCY_PROXY_PASSWORD`: A CI/CD password for logging in to the Dependency Proxy.
- `CI_DEPENDENCY_PROXY_SERVER`: The server for logging in to the Dependency Proxy.
-- `CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX`: The image prefix for pulling images through the Dependency Proxy.
+- `CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX`: the image prefix for pulling images through the
+ dependency proxy from the top-level group.
+- `CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX`: the image prefix for pulling images through the
+ dependency proxy from the direct group or subgroup that the project belongs to.
`CI_DEPENDENCY_PROXY_SERVER` and `CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX`
include the server port. If you explicitly include the Dependency Proxy
diff --git a/lib/api/internal/kubernetes.rb b/lib/api/internal/kubernetes.rb
index 7af5c2ad2ee..d1ad3c1feb1 100644
--- a/lib/api/internal/kubernetes.rb
+++ b/lib/api/internal/kubernetes.rb
@@ -100,6 +100,23 @@ module API
end
end
+ namespace 'kubernetes/agent_configuration' do
+ desc 'POST agent configuration' do
+ detail 'Store configuration for an agent'
+ end
+ params do
+ requires :agent_id, type: Integer, desc: 'ID of the configured Agent'
+ requires :agent_config, type: JSON, desc: 'Configuration for the Agent'
+ end
+ post '/' do
+ agent = Clusters::Agent.find(params[:agent_id])
+
+ Clusters::Agents::RefreshAuthorizationService.new(agent, config: params[:agent_config]).execute
+
+ no_content!
+ end
+ end
+
namespace 'kubernetes/usage_metrics' do
desc 'POST usage metrics' do
detail 'Updates usage metrics for agent'
diff --git a/lib/gitlab/database/background_migration/batched_job.rb b/lib/gitlab/database/background_migration/batched_job.rb
index 03bd02d7554..32765cb6a56 100644
--- a/lib/gitlab/database/background_migration/batched_job.rb
+++ b/lib/gitlab/database/background_migration/batched_job.rb
@@ -4,6 +4,7 @@ module Gitlab
module Database
module BackgroundMigration
class BatchedJob < ActiveRecord::Base # rubocop:disable Rails/ApplicationRecord
+ include EachBatch
include FromUnion
self.table_name = :batched_background_migration_jobs
diff --git a/lib/gitlab/database/background_migration/batched_migration.rb b/lib/gitlab/database/background_migration/batched_migration.rb
index 9d66824da51..d9fc2ea48f6 100644
--- a/lib/gitlab/database/background_migration/batched_migration.rb
+++ b/lib/gitlab/database/background_migration/batched_migration.rb
@@ -68,6 +68,17 @@ module Gitlab
)
end
+ def retry_failed_jobs!
+ batched_jobs.failed.each_batch(of: 100) do |batch|
+ self.class.transaction do
+ batch.lock.each(&:split_and_retry!)
+ self.active!
+ end
+ end
+
+ self.active!
+ end
+
def next_min_value
last_job&.max_value&.next || min_value
end
diff --git a/lib/sidebars/groups/menus/ci_cd_menu.rb b/lib/sidebars/groups/menus/ci_cd_menu.rb
index e870bbf5ebc..f5bce57f496 100644
--- a/lib/sidebars/groups/menus/ci_cd_menu.rb
+++ b/lib/sidebars/groups/menus/ci_cd_menu.rb
@@ -11,11 +11,6 @@ module Sidebars
true
end
- override :link
- def link
- renderable_items.first.link
- end
-
override :title
def title
_('CI/CD')
diff --git a/lib/sidebars/groups/menus/group_information_menu.rb b/lib/sidebars/groups/menus/group_information_menu.rb
index b28cb927ad2..9656811455e 100644
--- a/lib/sidebars/groups/menus/group_information_menu.rb
+++ b/lib/sidebars/groups/menus/group_information_menu.rb
@@ -13,11 +13,6 @@ module Sidebars
true
end
- override :link
- def link
- renderable_items.first.link
- end
-
override :title
def title
context.group.subgroup? ? _('Subgroup information') : _('Group information')
diff --git a/lib/sidebars/groups/menus/issues_menu.rb b/lib/sidebars/groups/menus/issues_menu.rb
index 95641c09076..5f0254a0529 100644
--- a/lib/sidebars/groups/menus/issues_menu.rb
+++ b/lib/sidebars/groups/menus/issues_menu.rb
@@ -17,11 +17,6 @@ module Sidebars
true
end
- override :link
- def link
- issues_group_path(context.group)
- end
-
override :title
def title
_('Issues')
diff --git a/lib/sidebars/groups/menus/packages_registries_menu.rb b/lib/sidebars/groups/menus/packages_registries_menu.rb
index e46e2820c04..e81e9355e7e 100644
--- a/lib/sidebars/groups/menus/packages_registries_menu.rb
+++ b/lib/sidebars/groups/menus/packages_registries_menu.rb
@@ -13,11 +13,6 @@ module Sidebars
true
end
- override :link
- def link
- renderable_items.first.link
- end
-
override :title
def title
_('Packages & Registries')
diff --git a/lib/sidebars/groups/menus/settings_menu.rb b/lib/sidebars/groups/menus/settings_menu.rb
index 8bc6077d302..f0239ca6a1a 100644
--- a/lib/sidebars/groups/menus/settings_menu.rb
+++ b/lib/sidebars/groups/menus/settings_menu.rb
@@ -19,11 +19,6 @@ module Sidebars
true
end
- override :link
- def link
- edit_group_path(context.group)
- end
-
override :title
def title
_('Settings')
diff --git a/lib/sidebars/menu.rb b/lib/sidebars/menu.rb
index 3b8872fd572..1af3d024291 100644
--- a/lib/sidebars/menu.rb
+++ b/lib/sidebars/menu.rb
@@ -33,10 +33,9 @@ module Sidebars
has_renderable_items? || menu_with_partial?
end
- # Menus might have or not a link
override :link
def link
- nil
+ renderable_items.first&.link
end
# This method normalizes the information retrieved from the submenus and this menu
diff --git a/lib/sidebars/projects/menus/analytics_menu.rb b/lib/sidebars/projects/menus/analytics_menu.rb
index 29fd0609596..b13b25d1cfe 100644
--- a/lib/sidebars/projects/menus/analytics_menu.rb
+++ b/lib/sidebars/projects/menus/analytics_menu.rb
@@ -21,7 +21,7 @@ module Sidebars
def link
return cycle_analytics_menu_item.link if cycle_analytics_menu_item.render?
- renderable_items.first.link
+ super
end
override :extra_container_html_options
diff --git a/lib/sidebars/projects/menus/ci_cd_menu.rb b/lib/sidebars/projects/menus/ci_cd_menu.rb
index f85a9faacd3..67e4209c382 100644
--- a/lib/sidebars/projects/menus/ci_cd_menu.rb
+++ b/lib/sidebars/projects/menus/ci_cd_menu.rb
@@ -15,11 +15,6 @@ module Sidebars
add_item(pipeline_schedules_menu_item)
end
- override :link
- def link
- project_pipelines_path(context.project)
- end
-
override :extra_container_html_options
def extra_container_html_options
{
diff --git a/lib/sidebars/projects/menus/deployments_menu.rb b/lib/sidebars/projects/menus/deployments_menu.rb
index fa6482562e8..110d78367b9 100644
--- a/lib/sidebars/projects/menus/deployments_menu.rb
+++ b/lib/sidebars/projects/menus/deployments_menu.rb
@@ -13,11 +13,6 @@ module Sidebars
true
end
- override :link
- def link
- renderable_items.first.link
- end
-
override :extra_container_html_options
def extra_container_html_options
{
diff --git a/lib/sidebars/projects/menus/infrastructure_menu.rb b/lib/sidebars/projects/menus/infrastructure_menu.rb
index aad1ce60d0e..e26bb2237e6 100644
--- a/lib/sidebars/projects/menus/infrastructure_menu.rb
+++ b/lib/sidebars/projects/menus/infrastructure_menu.rb
@@ -15,11 +15,6 @@ module Sidebars
true
end
- override :link
- def link
- renderable_items.first.link
- end
-
override :extra_container_html_options
def extra_container_html_options
{
diff --git a/lib/sidebars/projects/menus/issues_menu.rb b/lib/sidebars/projects/menus/issues_menu.rb
index fd57f21db88..3774bec2f13 100644
--- a/lib/sidebars/projects/menus/issues_menu.rb
+++ b/lib/sidebars/projects/menus/issues_menu.rb
@@ -18,11 +18,6 @@ module Sidebars
true
end
- override :link
- def link
- project_issues_path(context.project)
- end
-
override :extra_container_html_options
def extra_container_html_options
{
diff --git a/lib/sidebars/projects/menus/monitor_menu.rb b/lib/sidebars/projects/menus/monitor_menu.rb
index 0d7e0776d5b..59554726263 100644
--- a/lib/sidebars/projects/menus/monitor_menu.rb
+++ b/lib/sidebars/projects/menus/monitor_menu.rb
@@ -19,11 +19,6 @@ module Sidebars
true
end
- override :link
- def link
- renderable_items.first&.link
- end
-
override :extra_container_html_options
def extra_container_html_options
{
diff --git a/lib/sidebars/projects/menus/packages_registries_menu.rb b/lib/sidebars/projects/menus/packages_registries_menu.rb
index d49bb680853..f5f0da2992e 100644
--- a/lib/sidebars/projects/menus/packages_registries_menu.rb
+++ b/lib/sidebars/projects/menus/packages_registries_menu.rb
@@ -13,11 +13,6 @@ module Sidebars
true
end
- override :link
- def link
- renderable_items.first.link
- end
-
override :title
def title
_('Packages & Registries')
diff --git a/lib/sidebars/projects/menus/project_information_menu.rb b/lib/sidebars/projects/menus/project_information_menu.rb
index a5f06ebea20..44b94ee3522 100644
--- a/lib/sidebars/projects/menus/project_information_menu.rb
+++ b/lib/sidebars/projects/menus/project_information_menu.rb
@@ -13,11 +13,6 @@ module Sidebars
true
end
- override :link
- def link
- renderable_items.first.link
- end
-
override :extra_container_html_options
def extra_container_html_options
{ class: 'shortcuts-project-information' }
diff --git a/lib/sidebars/projects/menus/repository_menu.rb b/lib/sidebars/projects/menus/repository_menu.rb
index a784aecc3dc..0a295f0f618 100644
--- a/lib/sidebars/projects/menus/repository_menu.rb
+++ b/lib/sidebars/projects/menus/repository_menu.rb
@@ -20,11 +20,6 @@ module Sidebars
true
end
- override :link
- def link
- project_tree_path(context.project)
- end
-
override :extra_container_html_options
def extra_container_html_options
{
diff --git a/lib/sidebars/projects/menus/security_compliance_menu.rb b/lib/sidebars/projects/menus/security_compliance_menu.rb
index 5616b466560..9367514cdca 100644
--- a/lib/sidebars/projects/menus/security_compliance_menu.rb
+++ b/lib/sidebars/projects/menus/security_compliance_menu.rb
@@ -15,11 +15,6 @@ module Sidebars
true
end
- override :link
- def link
- renderable_items.first&.link
- end
-
override :title
def title
_('Security & Compliance')
diff --git a/lib/sidebars/projects/menus/settings_menu.rb b/lib/sidebars/projects/menus/settings_menu.rb
index 250143df649..ac41e534842 100644
--- a/lib/sidebars/projects/menus/settings_menu.rb
+++ b/lib/sidebars/projects/menus/settings_menu.rb
@@ -21,11 +21,6 @@ module Sidebars
true
end
- override :link
- def link
- edit_project_path(context.project)
- end
-
override :title
def title
_('Settings')
diff --git a/spec/factories/clusters/agents/group_authorizations.rb b/spec/factories/clusters/agents/group_authorizations.rb
new file mode 100644
index 00000000000..6ea3668dc66
--- /dev/null
+++ b/spec/factories/clusters/agents/group_authorizations.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :agent_group_authorization, class: 'Clusters::Agents::GroupAuthorization' do
+ association :agent, factory: :cluster_agent
+ group
+
+ config { { default_namespace: 'production' } }
+ end
+end
diff --git a/spec/features/admin/admin_sees_background_migrations_spec.rb b/spec/features/admin/admin_sees_background_migrations_spec.rb
index 11823195310..94fb3a0314f 100644
--- a/spec/features/admin/admin_sees_background_migrations_spec.rb
+++ b/spec/features/admin/admin_sees_background_migrations_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe "Admin > Admin sees background migrations" do
let_it_be(:finished_migration) { create(:batched_background_migration, table_name: 'finished', status: :finished) }
before_all do
- create(:batched_background_migration_job, batched_migration: failed_migration, batch_size: 30, status: :succeeded)
+ create(:batched_background_migration_job, batched_migration: failed_migration, batch_size: 10, min_value: 6, max_value: 15, status: :failed, attempts: 3)
end
before do
@@ -53,22 +53,35 @@ RSpec.describe "Admin > Admin sees background migrations" do
end
end
- it 'can view failed migrations' do
- visit admin_background_migrations_path
+ context 'when there are failed migrations' do
+ before do
+ allow_next_instance_of(Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy) do |batch_class|
+ allow(batch_class).to receive(:next_batch).with(anything, anything, batch_min_value: 6, batch_size: 5).and_return([6, 10])
+ end
+ end
- within '#content-body' do
- tab = find_link 'Failed'
- tab.click
+ it 'can view and retry them' do
+ visit admin_background_migrations_path
- expect(page).to have_current_path(admin_background_migrations_path(tab: 'failed'))
- expect(tab[:class]).to include('gl-tab-nav-item-active', 'gl-tab-nav-item-active-indigo')
+ within '#content-body' do
+ tab = find_link 'Failed'
+ tab.click
- expect(page).to have_selector('tbody tr', count: 1)
+ expect(page).to have_current_path(admin_background_migrations_path(tab: 'failed'))
+ expect(tab[:class]).to include('gl-tab-nav-item-active', 'gl-tab-nav-item-active-indigo')
+
+ expect(page).to have_selector('tbody tr', count: 1)
+
+ expect(page).to have_content(failed_migration.job_class_name)
+ expect(page).to have_content(failed_migration.table_name)
+ expect(page).to have_content('0.00%')
+ expect(page).to have_content(failed_migration.status.humanize)
- expect(page).to have_content(failed_migration.job_class_name)
- expect(page).to have_content(failed_migration.table_name)
- expect(page).to have_content('30.00%')
- expect(page).to have_content(failed_migration.status.humanize)
+ click_button('Retry')
+ expect(page).not_to have_content(failed_migration.job_class_name)
+ expect(page).not_to have_content(failed_migration.table_name)
+ expect(page).not_to have_content('0.00%')
+ end
end
end
diff --git a/spec/frontend/lib/utils/text_markdown_spec.js b/spec/frontend/lib/utils/text_markdown_spec.js
index 9fe2780fde2..beedb9b2eba 100644
--- a/spec/frontend/lib/utils/text_markdown_spec.js
+++ b/spec/frontend/lib/utils/text_markdown_spec.js
@@ -70,25 +70,6 @@ describe('init markdown', () => {
expect(textArea.value).toContain('# Does not parse the `$` currently.');
});
- it('inserts a new line correctly', () => {
- const initialValue = '';
-
- textArea.value = initialValue;
- textArea.selectionStart = 0;
- textArea.selectionEnd = 0;
-
- insertMarkdownText({
- textArea,
- text: textArea.value,
- tag: '```suggestion:-0+0\n{text}\n```',
- blockTag: true,
- selected: '# Does not parse the \\n currently.',
- wrap: false,
- });
-
- expect(textArea.value).toContain('# Does not parse the \\n currently.');
- });
-
it('inserts the tag on a new line if the current one is not empty', () => {
const initialValue = 'some text';
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
index 3207e97a639..a1c2634f59c 100644
--- a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
@@ -234,6 +234,42 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
end
end
+ describe '#retry_failed_jobs!' do
+ let(:batched_migration) { create(:batched_background_migration, status: 'failed') }
+
+ subject(:retry_failed_jobs) { batched_migration.retry_failed_jobs! }
+
+ context 'when there are failed migration jobs' do
+ let!(:batched_background_migration_job) { create(:batched_background_migration_job, batched_migration: batched_migration, batch_size: 10, min_value: 6, max_value: 15, status: :failed, attempts: 3) }
+
+ before do
+ allow_next_instance_of(Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy) do |batch_class|
+ allow(batch_class).to receive(:next_batch).with(anything, anything, batch_min_value: 6, batch_size: 5).and_return([6, 10])
+ end
+ end
+
+ it 'moves the status of the migration to active' do
+ retry_failed_jobs
+
+ expect(batched_migration.status).to eql 'active'
+ end
+
+ it 'changes the number of attempts to 0' do
+ retry_failed_jobs
+
+ expect(batched_background_migration_job.reload.attempts).to be_zero
+ end
+ end
+
+ context 'when there are no failed migration jobs' do
+ it 'moves the status of the migration to active' do
+ retry_failed_jobs
+
+ expect(batched_migration.status).to eql 'active'
+ end
+ end
+ end
+
describe '#job_class_name=' do
it_behaves_like 'an attr_writer that demodulizes assigned class names', :job_class_name
end
diff --git a/spec/lib/sidebars/menu_spec.rb b/spec/lib/sidebars/menu_spec.rb
index 1db80351e45..eb6a68f1afd 100644
--- a/spec/lib/sidebars/menu_spec.rb
+++ b/spec/lib/sidebars/menu_spec.rb
@@ -198,4 +198,27 @@ RSpec.describe Sidebars::Menu do
end
end
end
+
+ describe '#link' do
+ let(:foo_path) { '/foo_path'}
+
+ let(:foo_menu) do
+ ::Sidebars::MenuItem.new(
+ title: 'foo',
+ link: foo_path,
+ active_routes: {},
+ item_id: :foo
+ )
+ end
+
+ it 'returns first visible menu item link' do
+ menu.add_item(foo_menu)
+
+ expect(menu.link).to eq foo_path
+ end
+
+ it 'returns nil if there are no visible menu items' do
+ expect(menu.link).to be_nil
+ end
+ end
end
diff --git a/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb b/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb
index 381842be5ab..77efe99aaa9 100644
--- a/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb
@@ -49,25 +49,6 @@ RSpec.describe Sidebars::Projects::Menus::MonitorMenu do
end
end
- describe '#link' do
- let(:foo_path) { '/foo_path'}
-
- let(:foo_menu) do
- ::Sidebars::MenuItem.new(
- title: 'foo',
- link: foo_path,
- active_routes: {},
- item_id: :foo
- )
- end
-
- it 'returns first visible item link' do
- subject.insert_element_before(subject.renderable_items, subject.renderable_items.first.item_id, foo_menu)
-
- expect(subject.link).to eq foo_path
- end
- end
-
context 'Menu items' do
subject { described_class.new(context).renderable_items.index { |e| e.item_id == item_id } }
diff --git a/spec/migrations/add_default_project_approval_rules_vuln_allowed_spec.rb b/spec/migrations/add_default_project_approval_rules_vuln_allowed_spec.rb
new file mode 100644
index 00000000000..057e95eb158
--- /dev/null
+++ b/spec/migrations/add_default_project_approval_rules_vuln_allowed_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe AddDefaultProjectApprovalRulesVulnAllowed do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:namespace) { namespaces.create!(name: 'namespace', path: 'namespace') }
+ let(:project) { projects.create!(name: 'project', path: 'project', namespace_id: namespace.id) }
+ let(:approval_project_rules) { table(:approval_project_rules) }
+
+ it 'updates records when vulnerabilities_allowed is nil' do
+ records_to_migrate = 10
+
+ records_to_migrate.times do |i|
+ approval_project_rules.create!(name: "rule #{i}", project_id: project.id)
+ end
+
+ expect { migrate! }
+ .to change { approval_project_rules.where(vulnerabilities_allowed: nil).count }
+ .from(records_to_migrate)
+ .to(0)
+ end
+
+ it 'defaults vulnerabilities_allowed to 0' do
+ approval_project_rule = approval_project_rules.create!(name: "new rule", project_id: project.id)
+
+ expect(approval_project_rule.vulnerabilities_allowed).to be_nil
+
+ migrate!
+
+ expect(approval_project_rule.reload.vulnerabilities_allowed).to eq(0)
+ end
+end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 26abc98656e..290dc7df430 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -2632,6 +2632,10 @@ RSpec.describe Ci::Build do
value: "#{Gitlab.host_with_port}/#{project.namespace.root_ancestor.path.downcase}#{DependencyProxy::URL_SUFFIX}",
public: true,
masked: false },
+ { key: 'CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX',
+ value: "#{Gitlab.host_with_port}/#{project.namespace.full_path.downcase}#{DependencyProxy::URL_SUFFIX}",
+ public: true,
+ masked: false },
{ key: 'CI_API_V4_URL', value: 'http://localhost/api/v4', public: true, masked: false },
{ key: 'CI_PIPELINE_IID', value: pipeline.iid.to_s, public: true, masked: false },
{ key: 'CI_PIPELINE_SOURCE', value: pipeline.source, public: true, masked: false },
diff --git a/spec/models/clusters/agent_spec.rb b/spec/models/clusters/agent_spec.rb
index ea7a55480a8..c8fc09565a9 100644
--- a/spec/models/clusters/agent_spec.rb
+++ b/spec/models/clusters/agent_spec.rb
@@ -9,6 +9,8 @@ RSpec.describe Clusters::Agent do
it { is_expected.to belong_to(:project).class_name('::Project') }
it { is_expected.to have_many(:agent_tokens).class_name('Clusters::AgentToken') }
it { is_expected.to have_many(:last_used_agent_tokens).class_name('Clusters::AgentToken') }
+ it { is_expected.to have_many(:group_authorizations).class_name('Clusters::Agents::GroupAuthorization') }
+ it { is_expected.to have_many(:authorized_groups).through(:group_authorizations) }
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_length_of(:name).is_at_most(63) }
diff --git a/spec/models/clusters/agents/group_authorization_spec.rb b/spec/models/clusters/agents/group_authorization_spec.rb
new file mode 100644
index 00000000000..2a99fb26e3f
--- /dev/null
+++ b/spec/models/clusters/agents/group_authorization_spec.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Clusters::Agents::GroupAuthorization do
+ it { is_expected.to belong_to(:agent).class_name('Clusters::Agent').required }
+ it { is_expected.to belong_to(:group).class_name('::Group').required }
+
+ it { expect(described_class).to validate_jsonb_schema(['config']) }
+end
diff --git a/spec/models/internal_id_spec.rb b/spec/models/internal_id_spec.rb
index 6aba91d9471..a8a5ac98a5a 100644
--- a/spec/models/internal_id_spec.rb
+++ b/spec/models/internal_id_spec.rb
@@ -238,16 +238,48 @@ RSpec.describe InternalId do
end
end
- context 'when the feature flag is disabled' do
- stub_feature_flags(generate_iids_without_explicit_locking: false)
+ context 'when the explicit locking feature flag is disabled' do
+ before do
+ stub_feature_flags(generate_iids_without_explicit_locking: false)
+ end
- it_behaves_like 'a monotonically increasing id generator'
+ context 'when the insert all feature flag is enabled' do
+ before do
+ stub_feature_flags(use_insert_all_in_internal_id: true)
+ end
+
+ it_behaves_like 'a monotonically increasing id generator'
+ end
+
+ context 'when the insert all feature flag is disabled' do
+ before do
+ stub_feature_flags(use_insert_all_in_internal_id: false)
+ end
+
+ it_behaves_like 'a monotonically increasing id generator'
+ end
end
- context 'when the feature flag is enabled' do
- stub_feature_flags(generate_iids_without_explicit_locking: true)
+ context 'when the explicit locking feature flag is enabled' do
+ before do
+ stub_feature_flags(generate_iids_without_explicit_locking: true)
+ end
- it_behaves_like 'a monotonically increasing id generator'
+ context 'when the insert all feature flag is enabled' do
+ before do
+ stub_feature_flags(use_insert_all_in_internal_id: true)
+ end
+
+ it_behaves_like 'a monotonically increasing id generator'
+ end
+
+ context 'when the insert all feature flag is disabled' do
+ before do
+ stub_feature_flags(use_insert_all_in_internal_id: false)
+ end
+
+ it_behaves_like 'a monotonically increasing id generator'
+ end
end
describe '#increment_and_save!' do
diff --git a/spec/requests/admin/background_migrations_controller_spec.rb b/spec/requests/admin/background_migrations_controller_spec.rb
new file mode 100644
index 00000000000..c7d5d5cae08
--- /dev/null
+++ b/spec/requests/admin/background_migrations_controller_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Admin::BackgroundMigrationsController, :enable_admin_mode do
+ let(:admin) { create(:admin) }
+
+ before do
+ sign_in(admin)
+ end
+
+ describe 'POST #retry' do
+ let(:migration) { create(:batched_background_migration, status: 'failed') }
+
+ before do
+ create(:batched_background_migration_job, batched_migration: migration, batch_size: 10, min_value: 6, max_value: 15, status: :failed, attempts: 3)
+
+ allow_next_instance_of(Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy) do |batch_class|
+ allow(batch_class).to receive(:next_batch).with(anything, anything, batch_min_value: 6, batch_size: 5).and_return([6, 10])
+ end
+ end
+
+ subject(:retry_migration) { post retry_admin_background_migration_path(migration) }
+
+ it 'redirects the user to the admin migrations page' do
+ retry_migration
+
+ expect(response).to redirect_to(admin_background_migrations_path)
+ end
+
+ it 'retries the migration' do
+ retry_migration
+
+ expect(migration.reload.status).to eql 'active'
+ end
+
+ context 'when the migration is not failed' do
+ let(:migration) { create(:batched_background_migration, status: 'paused') }
+
+ it 'keeps the same migration status' do
+ expect { retry_migration }.not_to change { migration.reload.status }
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/internal/kubernetes_spec.rb b/spec/requests/api/internal/kubernetes_spec.rb
index 2acf6951d50..14d5b8d4744 100644
--- a/spec/requests/api/internal/kubernetes_spec.rb
+++ b/spec/requests/api/internal/kubernetes_spec.rb
@@ -93,6 +93,44 @@ RSpec.describe API::Internal::Kubernetes do
end
end
+ describe 'POST /internal/kubernetes/agent_configuration' do
+ def send_request(headers: {}, params: {})
+ post api('/internal/kubernetes/agent_configuration'), params: params, headers: headers.reverse_merge(jwt_auth_headers)
+ end
+
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, namespace: group) }
+ let_it_be(:agent) { create(:cluster_agent, project: project) }
+ let_it_be(:config) do
+ {
+ ci_access: {
+ groups: [
+ { id: group.full_path, default_namespace: 'production' }
+ ]
+ }
+ }
+ end
+
+ include_examples 'authorization'
+
+ context 'agent exists' do
+ it 'configures the agent and returns a 204' do
+ send_request(params: { agent_id: agent.id, agent_config: config })
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ expect(agent.authorized_groups).to contain_exactly(group)
+ end
+ end
+
+ context 'agent does not exist' do
+ it 'returns a 404' do
+ send_request(params: { agent_id: -1, agent_config: config })
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
describe 'GET /internal/kubernetes/agent_info' do
def send_request(headers: {}, params: {})
get api('/internal/kubernetes/agent_info'), params: params, headers: headers.reverse_merge(jwt_auth_headers)
diff --git a/spec/services/clusters/agents/refresh_authorization_service_spec.rb b/spec/services/clusters/agents/refresh_authorization_service_spec.rb
new file mode 100644
index 00000000000..8a887b347bb
--- /dev/null
+++ b/spec/services/clusters/agents/refresh_authorization_service_spec.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Clusters::Agents::RefreshAuthorizationService do
+ describe '#execute' do
+ let_it_be(:root_ancestor) { create(:group) }
+ let_it_be(:removed_group) { create(:group, parent: root_ancestor) }
+ let_it_be(:modified_group) { create(:group, parent: root_ancestor) }
+ let_it_be(:added_group) { create(:group, parent: root_ancestor) }
+
+ let(:project) { create(:project, namespace: root_ancestor) }
+ let(:agent) { create(:cluster_agent, project: project) }
+
+ let(:config) do
+ {
+ ci_access: {
+ groups: [
+ { id: added_group.full_path, default_namespace: 'default' },
+ { id: modified_group.full_path, default_namespace: 'new-namespace' }
+ ]
+ }
+ }.deep_stringify_keys
+ end
+
+ subject { described_class.new(agent, config: config).execute }
+
+ before do
+ default_config = { default_namespace: 'default' }
+
+ agent.group_authorizations.create!(group: removed_group, config: default_config)
+ agent.group_authorizations.create!(group: modified_group, config: default_config)
+ end
+
+ it 'refreshes authorizations for the agent' do
+ expect(subject).to be_truthy
+ expect(agent.authorized_groups).to contain_exactly(added_group, modified_group)
+
+ added_authorization = agent.group_authorizations.find_by(group: added_group)
+ expect(added_authorization.config).to eq({ 'default_namespace' => 'default' })
+
+ modified_authorization = agent.group_authorizations.find_by(group: modified_group)
+ expect(modified_authorization.config).to eq({ 'default_namespace' => 'new-namespace' })
+ end
+
+ context 'config contains no groups' do
+ let(:config) { {} }
+
+ it 'removes all authorizations' do
+ expect(subject).to be_truthy
+ expect(agent.authorized_groups).to be_empty
+ end
+ end
+
+ context 'config contains groups outside of the configuration project hierarchy' do
+ let(:project) { create(:project, namespace: create(:group)) }
+
+ it 'removes all authorizations' do
+ expect(subject).to be_truthy
+ expect(agent.authorized_groups).to be_empty
+ end
+ end
+
+ context 'configuration project does not belong to a group' do
+ let(:project) { create(:project) }
+
+ it 'removes all authorizations' do
+ expect(subject).to be_truthy
+ expect(agent.authorized_groups).to be_empty
+ end
+ end
+
+ context 'config contains too many groups' do
+ before do
+ stub_const("#{described_class}::AUTHORIZED_GROUP_LIMIT", 1)
+ end
+
+ it 'authorizes groups up to the limit' do
+ expect(subject).to be_truthy
+ expect(agent.authorized_groups).to contain_exactly(added_group)
+ end
+ end
+ end
+end