diff options
44 files changed, 562 insertions, 50 deletions
diff --git a/.gitlab/issue_templates/Geo Replicate a new Git repository type.md b/.gitlab/issue_templates/Geo Replicate a new Git repository type.md index 549100741d8..95f92430eea 100644 --- a/.gitlab/issue_templates/Geo Replicate a new Git repository type.md +++ b/.gitlab/issue_templates/Geo Replicate a new Git repository type.md @@ -588,7 +588,7 @@ Metrics are gathered by `Geo::MetricsUpdateWorker`, persisted in `GeoNodeStatus` - `cool_widgets_synced_in_percentage` - `cool_widgets_verified_in_percentage` - [ ] Add the same fields to `GET /geo_nodes/status` example response in - `ee/spec/fixtures/api/schemas/public_api/v4/geo_node_status.json`. + `ee/spec/fixtures/api/schemas/public_api/v4/geo_node_status.json` and `ee/spec/fixtures/api/schemas/public_api/v4/geo_site_status.json`. - [ ] Add the following fields to the `Sidekiq metrics` table in `doc/administration/monitoring/prometheus/gitlab_metrics.md`: ```markdown | `geo_cool_widgets` | Gauge | XX.Y | Number of Cool Widgets on primary | `url` | diff --git a/.gitlab/issue_templates/Geo Replicate a new blob type.md b/.gitlab/issue_templates/Geo Replicate a new blob type.md index fc454919cec..7bf8dd6526e 100644 --- a/.gitlab/issue_templates/Geo Replicate a new blob type.md +++ b/.gitlab/issue_templates/Geo Replicate a new blob type.md @@ -555,7 +555,7 @@ Metrics are gathered by `Geo::MetricsUpdateWorker`, persisted in `GeoNodeStatus` - `cool_widgets_synced_in_percentage` - `cool_widgets_verified_in_percentage` - [ ] Add the same fields to `GET /geo_nodes/status` example response in - `ee/spec/fixtures/api/schemas/public_api/v4/geo_node_status.json`. + `ee/spec/fixtures/api/schemas/public_api/v4/geo_node_status.json` and `ee/spec/fixtures/api/schemas/public_api/v4/geo_site_status.json`. - [ ] Add the following fields to the `Sidekiq metrics` table in `doc/administration/monitoring/prometheus/gitlab_metrics.md`: ```markdown diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb index f1c5d5e08ad..f7ee90ab870 100644 --- a/app/finders/merge_requests_finder.rb +++ b/app/finders/merge_requests_finder.rb @@ -158,19 +158,15 @@ class MergeRequestsFinder < IssuableFinder return items if draft_param.nil? if draft_param - items.where(wip_match(items.arel_table)) + items.where(draft_match(items.arel_table)) else - items.where.not(wip_match(items.arel_table)) + items.where.not(draft_match(items.arel_table)) end end # rubocop: enable CodeReuse/ActiveRecord - # WIP is deprecated in favor of Draft. Currently both options are supported - def wip_match(table) - table[:title].matches('WIP:%') - .or(table[:title].matches('WIP %')) - .or(table[:title].matches('[WIP]%')) - .or(table[:title].matches('Draft - %')) + def draft_match(table) + table[:title].matches('Draft - %') .or(table[:title].matches('Draft:%')) .or(table[:title].matches('[Draft]%')) .or(table[:title].matches('(Draft)%')) diff --git a/app/models/clusters/agent.rb b/app/models/clusters/agent.rb index 6980ec1c2d3..a03649cc41f 100644 --- a/app/models/clusters/agent.rb +++ b/app/models/clusters/agent.rb @@ -29,6 +29,8 @@ module Clusters has_many :activity_events, -> { in_timeline_order }, class_name: 'Clusters::Agents::ActivityEvent', inverse_of: :agent + has_many :environments, class_name: '::Environment', inverse_of: :cluster_agent, foreign_key: :cluster_agent_id + scope :ordered_by_name, -> { order(:name) } scope :with_name, -> (name) { where(name: name) } scope :has_vulnerabilities, -> (value = true) { where(has_vulnerabilities: value) } diff --git a/app/models/design_management/repository.rb b/app/models/design_management/repository.rb index 33c5dc15fa4..39077fdbcb1 100644 --- a/app/models/design_management/repository.rb +++ b/app/models/design_management/repository.rb @@ -34,3 +34,5 @@ module DesignManagement end end end + +DesignManagement::Repository.prepend_mod_with('DesignManagement::Repository') diff --git a/app/models/environment.rb b/app/models/environment.rb index f1de41674c6..e34b43d0a7c 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -15,6 +15,7 @@ class Environment < ApplicationRecord belongs_to :project, optional: false belongs_to :merge_request, optional: true + belongs_to :cluster_agent, class_name: 'Clusters::Agent', optional: true, inverse_of: :environments use_fast_destroy :all_deployments nullify_if_blank :external_url diff --git a/app/models/project.rb b/app/models/project.rb index bd341b83482..462a169ea95 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1205,8 +1205,8 @@ class Project < ApplicationRecord @repository ||= Gitlab::GlRepository::PROJECT.repository_for(self) end - def design_management_repository - super || create_design_management_repository + def find_or_create_design_management_repository + design_management_repository || create_design_management_repository end def design_repository diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb index 676a834d79d..4971dc3775f 100644 --- a/app/workers/post_receive.rb +++ b/app/workers/post_receive.rb @@ -36,6 +36,8 @@ class PostReceive process_project_changes(post_received, container) elsif repo_type.snippet? process_snippet_changes(post_received, container) + elsif repo_type.design? + process_design_management_repository_changes(post_received, container) else # Other repos don't have hooks for now end @@ -89,10 +91,23 @@ class PostReceive Snippets::UpdateStatisticsService.new(snippet).execute end + def process_design_management_repository_changes(post_received, design_management_repository) + user = identify_user(post_received) + + return false unless user + + replicate_design_management_repository_changes(design_management_repository) + expire_caches(post_received, design_management_repository.repository) + end + def replicate_snippet_changes(snippet) # Used by Gitlab Geo end + def replicate_design_management_repository_changes(design_management_repository) + # Used by GitLab Geo + end + # Expire the repository status, branch, and tag cache once per push. def expire_caches(post_received, repository) repository.expire_status_cache if repository.empty? diff --git a/config/application.rb b/config/application.rb index e7bea556960..5aca9b999a7 100644 --- a/config/application.rb +++ b/config/application.rb @@ -70,6 +70,7 @@ module Gitlab require_dependency Rails.root.join('lib/gitlab/middleware/rack_multipart_tempfile_factory') require_dependency Rails.root.join('lib/gitlab/runtime') require_dependency Rails.root.join('lib/gitlab/patch/database_config') + require_dependency Rails.root.join('lib/gitlab/patch/redis_cache_store') require_dependency Rails.root.join('lib/gitlab/exceptions_app') config.exceptions_app = Gitlab::ExceptionsApp.new(Gitlab.jh? ? Rails.root.join('jh/public') : Rails.public_path) @@ -484,6 +485,7 @@ module Gitlab end # Use caching across all environments + ActiveSupport::Cache::RedisCacheStore.prepend(Gitlab::Patch::RedisCacheStore) config.cache_store = :redis_cache_store, Gitlab::Redis::Cache.active_support_config config.active_job.queue_adapter = :sidekiq diff --git a/config/feature_flags/development/enable_rails_cache_pipeline_patch.yml b/config/feature_flags/development/enable_rails_cache_pipeline_patch.yml new file mode 100644 index 00000000000..fdbd0eaaa76 --- /dev/null +++ b/config/feature_flags/development/enable_rails_cache_pipeline_patch.yml @@ -0,0 +1,8 @@ +--- +name: enable_rails_cache_pipeline_patch +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119724 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/410115 +milestone: '16.1' +type: development +group: group::scalability +default_enabled: false diff --git a/config/feature_flags/development/load_merge_request_via_links.yml b/config/feature_flags/development/load_merge_request_via_links.yml new file mode 100644 index 00000000000..a9db587a7a9 --- /dev/null +++ b/config/feature_flags/development/load_merge_request_via_links.yml @@ -0,0 +1,8 @@ +--- +name: load_merge_request_via_links +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117321 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/412177 +milestone: '16.1' +type: development +group: group::threat insights +default_enabled: false diff --git a/config/initializers_before_autoloader/000_inflections.rb b/config/initializers_before_autoloader/000_inflections.rb index 2bf98a38544..41ffa454b4f 100644 --- a/config/initializers_before_autoloader/000_inflections.rb +++ b/config/initializers_before_autoloader/000_inflections.rb @@ -19,6 +19,7 @@ ActiveSupport::Inflector.inflections do |inflect| container_repository_registry dependency_proxy_blob_registry design_registry + design_management_repository_registry dependency_proxy_manifest_registry event_log file_registry diff --git a/db/docs/design_management_repository_states.yml b/db/docs/design_management_repository_states.yml index 3a8fa59fb42..82378f692dd 100644 --- a/db/docs/design_management_repository_states.yml +++ b/db/docs/design_management_repository_states.yml @@ -1,7 +1,7 @@ --- table_name: design_management_repository_states classes: -classes: [] +- Geo::DesignManagementRepositoryState feature_categories: - geo_replication description: Separate table for Design Repository verification states diff --git a/db/migrate/20230517151041_add_agent_id_column_to_environments.rb b/db/migrate/20230517151041_add_agent_id_column_to_environments.rb new file mode 100644 index 00000000000..bc042d361bc --- /dev/null +++ b/db/migrate/20230517151041_add_agent_id_column_to_environments.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddAgentIdColumnToEnvironments < Gitlab::Database::Migration[2.1] + enable_lock_retries! + + def change + add_column :environments, :cluster_agent_id, :bigint, null: true + end +end diff --git a/db/migrate/20230517151141_add_index_to_agent_id_column_environments.rb b/db/migrate/20230517151141_add_index_to_agent_id_column_environments.rb new file mode 100644 index 00000000000..7a88ea7cfe6 --- /dev/null +++ b/db/migrate/20230517151141_add_index_to_agent_id_column_environments.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class AddIndexToAgentIdColumnEnvironments < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + INDEX_NAME = 'index_environments_cluster_agent_id' + + def up + add_concurrent_index :environments, :cluster_agent_id, name: INDEX_NAME, where: 'cluster_agent_id IS NOT NULL' + end + + def down + remove_concurrent_index_by_name :environments, name: INDEX_NAME + end +end diff --git a/db/migrate/20230517151241_add_agent_id_foreign_key_to_environments.rb b/db/migrate/20230517151241_add_agent_id_foreign_key_to_environments.rb new file mode 100644 index 00000000000..c12bff0ec4e --- /dev/null +++ b/db/migrate/20230517151241_add_agent_id_foreign_key_to_environments.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddAgentIdForeignKeyToEnvironments < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + def up + add_concurrent_foreign_key :environments, :cluster_agents, + column: :cluster_agent_id, + on_delete: :nullify + end + + def down + with_lock_retries do + remove_foreign_key :environments, column: :cluster_agent_id + end + end +end diff --git a/db/schema_migrations/20230517151041 b/db/schema_migrations/20230517151041 new file mode 100644 index 00000000000..acd95a39933 --- /dev/null +++ b/db/schema_migrations/20230517151041 @@ -0,0 +1 @@ +ce12aa5cb3f207f2bfa19a27a1765ad027f825b6d75e7029fee5141e3160e5cb
\ No newline at end of file diff --git a/db/schema_migrations/20230517151141 b/db/schema_migrations/20230517151141 new file mode 100644 index 00000000000..658a5798332 --- /dev/null +++ b/db/schema_migrations/20230517151141 @@ -0,0 +1 @@ +06246b8d7df8d18c3d8e5308f1dcc8375e011f507a969c95678aa8d532a24644
\ No newline at end of file diff --git a/db/schema_migrations/20230517151241 b/db/schema_migrations/20230517151241 new file mode 100644 index 00000000000..fc08cf92ae8 --- /dev/null +++ b/db/schema_migrations/20230517151241 @@ -0,0 +1 @@ +f528675db7c4884930b4febc91aca6105189cd040416b92e21364ffe9d9e9bfd
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 4bd77584c6b..87510ecc9e7 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -15661,7 +15661,8 @@ CREATE TABLE environments ( auto_stop_at timestamp with time zone, auto_delete_at timestamp with time zone, tier smallint, - merge_request_id bigint + merge_request_id bigint, + cluster_agent_id bigint ); CREATE SEQUENCE environments_id_seq @@ -30635,6 +30636,8 @@ CREATE INDEX index_emails_on_user_id ON emails USING btree (user_id); CREATE INDEX index_enabled_clusters_on_id ON clusters USING btree (id) WHERE (enabled = true); +CREATE INDEX index_environments_cluster_agent_id ON environments USING btree (cluster_agent_id) WHERE (cluster_agent_id IS NOT NULL); + CREATE INDEX index_environments_for_name_search_within_folder ON environments USING btree (project_id, lower(ltrim((name)::text, ((environment_type)::text || '/'::text))) varchar_pattern_ops, state); CREATE INDEX index_environments_on_merge_request_id ON environments USING btree (merge_request_id); @@ -35056,6 +35059,9 @@ ALTER TABLE ONLY index_statuses ALTER TABLE ONLY cluster_agent_tokens ADD CONSTRAINT fk_75008f3553 FOREIGN KEY (created_by_user_id) REFERENCES users(id) ON DELETE SET NULL; +ALTER TABLE ONLY environments + ADD CONSTRAINT fk_75c2098045 FOREIGN KEY (cluster_agent_id) REFERENCES cluster_agents(id) ON DELETE SET NULL; + ALTER TABLE ONLY vulnerabilities ADD CONSTRAINT fk_76bc5f5455 FOREIGN KEY (resolved_by_id) REFERENCES users(id) ON DELETE SET NULL; diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md index 6b6712cdd01..f92b408323c 100644 --- a/doc/administration/monitoring/prometheus/gitlab_metrics.md +++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md @@ -372,6 +372,16 @@ configuration option in `gitlab.yml`. These metrics are served from the | `gitlab_memwd_violations_handled_total` | Counter | 15.9 | Total number of times Sidekiq process memory violations were handled | | | `sidekiq_watchdog_running_jobs_total` | Counter | 15.9 | Current running jobs when RSS limit was reached | `worker_class` | | `gitlab_maintenance_mode` | Gauge | 15.11 | Is GitLab Maintenance Mode enabled? | | +| `geo_design_management_repositories` | Gauge | 16.1 | Number of design repositories on primary | `url` | +| `geo_design_management_repositories_checksum_total` | Gauge | 16.1 | Number of design repositories tried to checksum on primary | `url` | +| `geo_design_management_repositories_checksummed` | Gauge | 16.1 | Number of design repositories successfully checksummed on primary | `url` | +| `geo_design_management_repositories_checksum_failed` | Gauge | 16.1 | Number of design repositories failed to calculate the checksum on primary | `url` | +| `geo_design_management_repositories_synced` | Gauge | 16.1 | Number of syncable design repositories synced on secondary | `url` | +| `geo_design_management_repositories_failed` | Gauge | 16.1 | Number of syncable design repositories failed to sync on secondary | `url` | +| `geo_design_management_repositories_registry` | Gauge | 16.1 | Number of design repositories in the registry | `url` | +| `geo_design_management_repositories_verification_total` | Gauge | 16.1 | Number of design repositories verifications tried on secondary | `url` | +| `geo_design_management_repositories_verified` | Gauge | 16.1 | Number of design repositories verified on secondary | `url` | +| `geo_design_management_repositories_verification_failed` | Gauge | 16.1 | Number of design repositories verifications failed on secondary | `url` | ## Database load balancing metrics **(PREMIUM SELF)** diff --git a/doc/administration/server_hooks.md b/doc/administration/server_hooks.md index b167412075b..c6621c59c8f 100644 --- a/doc/administration/server_hooks.md +++ b/doc/administration/server_hooks.md @@ -201,7 +201,7 @@ The following GitLab environment variables are supported for all server hooks: | Environment variable | Description | |:---------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------| -| `GL_ID` | GitLab identifier of user that initiated the push. For example, `user-2234`. | +| `GL_ID` | GitLab identifier of user or SSH key that initiated the push. For example, `user-2234` or `key-4`. | | `GL_PROJECT_PATH` | (GitLab 13.2 and later) GitLab project path. | | `GL_PROTOCOL` | (GitLab 13.2 and later) Protocol used for this change. One of: `http` (Git `push` using HTTP), `ssh` (Git `push` using SSH), or `web` (all other actions). | | `GL_REPOSITORY` | `project-<id>` where `id` is the ID of the project. | diff --git a/doc/api/geo_nodes.md b/doc/api/geo_nodes.md index 038b7d633a6..1445e9768c1 100644 --- a/doc/api/geo_nodes.md +++ b/doc/api/geo_nodes.md @@ -550,7 +550,19 @@ Example response: "dependency_proxy_manifests_verified_count": 5, "dependency_proxy_manifests_verification_failed_count": 5, "dependency_proxy_manifests_synced_in_percentage": "100.00%", - "dependency_proxy_manifests_verified_in_percentage": "100.00%" + "dependency_proxy_manifests_verified_in_percentage": "100.00%", + "design_management_repositories_count": 5, + "design_management_repositories_checksum_total_count": 5, + "design_management_repositories_checksummed_count": 5, + "design_management_repositories_checksum_failed_count": 5, + "design_management_repositories_synced_count": 5, + "design_management_repositories_failed_count": 0, + "design_management_repositories_registry_count": 5, + "design_management_repositories_verification_total_count": 5, + "design_management_repositories_verified_count": 5, + "design_management_repositories_verification_failed_count": 5, + "design_management_repositories_synced_in_percentage": "100.00%", + "design_management_repositories_verified_in_percentage": "100.00%" }, { "geo_node_id": 2, @@ -580,6 +592,18 @@ Example response: "design_repositories_synced_count": null, "design_repositories_failed_count": null, "design_repositories_synced_in_percentage": "0.00%", + "design_management_repositories_count": 5, + "design_management_repositories_synced_count": 5, + "design_management_repositories_failed_count": 5, + "design_management_repositories_synced_in_percentage": "100.00%", + "design_management_repositories_checksum_total_count": 5, + "design_management_repositories_checksummed_count": 5, + "design_management_repositories_checksum_failed_count": 5, + "design_management_repositories_registry_count": 5, + "design_management_repositories_verification_total_count": 5, + "design_management_repositories_verified_count": 5, + "design_management_repositories_verification_failed_count": 5, + "design_management_repositories_verified_in_percentage": "100.00%", "projects_count": 41, "repositories_count": 41, "repositories_failed_count": 1, @@ -966,7 +990,19 @@ Example response: "dependency_proxy_manifests_verified_count": 5, "dependency_proxy_manifests_verification_failed_count": 5, "dependency_proxy_manifests_synced_in_percentage": "100.00%", - "dependency_proxy_manifests_verified_in_percentage": "100.00%" + "dependency_proxy_manifests_verified_in_percentage": "100.00%", + "design_management_repositories_count": 5, + "design_management_repositories_checksum_total_count": 5, + "design_management_repositories_checksummed_count": 5, + "design_management_repositories_checksum_failed_count": 5, + "design_management_repositories_synced_count": 5, + "design_management_repositories_failed_count": 0, + "design_management_repositories_registry_count": 5, + "design_management_repositories_verification_total_count": 5, + "design_management_repositories_verified_count": 5, + "design_management_repositories_verification_failed_count": 5, + "design_management_repositories_synced_in_percentage": "100.00%", + "design_management_repositories_verified_in_percentage": "100.00%" } ``` diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 43fd2490b14..aba5ea4de71 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -8628,6 +8628,29 @@ The edge type for [`Design`](#design). | <a id="designedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. | | <a id="designedgenode"></a>`node` | [`Design`](#design) | The item at the end of the edge. | +#### `DesignManagementRepositoryRegistryConnection` + +The connection type for [`DesignManagementRepositoryRegistry`](#designmanagementrepositoryregistry). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="designmanagementrepositoryregistryconnectionedges"></a>`edges` | [`[DesignManagementRepositoryRegistryEdge]`](#designmanagementrepositoryregistryedge) | A list of edges. | +| <a id="designmanagementrepositoryregistryconnectionnodes"></a>`nodes` | [`[DesignManagementRepositoryRegistry]`](#designmanagementrepositoryregistry) | A list of nodes. | +| <a id="designmanagementrepositoryregistryconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | + +#### `DesignManagementRepositoryRegistryEdge` + +The edge type for [`DesignManagementRepositoryRegistry`](#designmanagementrepositoryregistry). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="designmanagementrepositoryregistryedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. | +| <a id="designmanagementrepositoryregistryedgenode"></a>`node` | [`DesignManagementRepositoryRegistry`](#designmanagementrepositoryregistry) | The item at the end of the edge. | + #### `DesignVersionConnection` The connection type for [`DesignVersion`](#designversion). @@ -13868,6 +13891,25 @@ Returns [`DesignVersion`](#designversion). | ---- | ---- | ----------- | | <a id="designmanagementversionid"></a>`id` | [`DesignManagementVersionID!`](#designmanagementversionid) | Global ID of the version. | +### `DesignManagementRepositoryRegistry` + +Represents the Geo replication and verification state of a Design Management Repository. + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="designmanagementrepositoryregistrycreatedat"></a>`createdAt` | [`Time`](#time) | Timestamp when the DesignManagementRepositoryRegistry was created. | +| <a id="designmanagementrepositoryregistrydesignmanagementrepositoryid"></a>`designManagementRepositoryId` | [`ID!`](#id) | ID of the Design Management Repository. | +| <a id="designmanagementrepositoryregistryid"></a>`id` | [`ID!`](#id) | ID of the DesignManagementRepositoryRegistry. | +| <a id="designmanagementrepositoryregistrylastsyncfailure"></a>`lastSyncFailure` | [`String`](#string) | Error message during sync of the DesignManagementRepositoryRegistry. | +| <a id="designmanagementrepositoryregistrylastsyncedat"></a>`lastSyncedAt` | [`Time`](#time) | Timestamp of the most recent successful sync of the DesignManagementRepositoryRegistry. | +| <a id="designmanagementrepositoryregistryretryat"></a>`retryAt` | [`Time`](#time) | Timestamp after which the DesignManagementRepositoryRegistry is resynced. | +| <a id="designmanagementrepositoryregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the DesignManagementRepositoryRegistry. | +| <a id="designmanagementrepositoryregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the DesignManagementRepositoryRegistry. | +| <a id="designmanagementrepositoryregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the DesignManagementRepositoryRegistry is reverified. | +| <a id="designmanagementrepositoryregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the DesignManagementRepositoryRegistry. | + ### `DesignVersion` A specific version in which designs were added, modified or deleted. @@ -14880,6 +14922,29 @@ four standard [pagination arguments](#connection-pagination-arguments): | <a id="geonodedependencyproxymanifestregistriesreplicationstate"></a>`replicationState` | [`ReplicationStateEnum`](#replicationstateenum) | Filters registries by their replication state. | | <a id="geonodedependencyproxymanifestregistriesverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Filters registries by their verification state. | +##### `GeoNode.designManagementRepositoryRegistries` + +Find Design Repository registries on this Geo node. Ignored if `geo_design_management_repository_replication` feature flag is disabled. + +WARNING: +**Introduced** in 16.1. +This feature is an Experiment. It can be changed or removed at any time. + +Returns [`DesignManagementRepositoryRegistryConnection`](#designmanagementrepositoryregistryconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="geonodedesignmanagementrepositoryregistriesids"></a>`ids` | [`[ID!]`](#id) | Filters registries by their ID. | +| <a id="geonodedesignmanagementrepositoryregistrieskeyword"></a>`keyword` | [`String`](#string) | Filters registries by their attributes using a keyword. | +| <a id="geonodedesignmanagementrepositoryregistriesreplicationstate"></a>`replicationState` | [`ReplicationStateEnum`](#replicationstateenum) | Filters registries by their replication state. | +| <a id="geonodedesignmanagementrepositoryregistriesverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Filters registries by their verification state. | + ##### `GeoNode.groupWikiRepositoryRegistries` Find group wiki repository registries on this Geo node. @@ -24562,6 +24627,7 @@ Geo registry class. | <a id="georegistryclasscontainer_repository_registry"></a>`CONTAINER_REPOSITORY_REGISTRY` | Geo::ContainerRepositoryRegistry registry class. | | <a id="georegistryclassdependency_proxy_blob_registry"></a>`DEPENDENCY_PROXY_BLOB_REGISTRY` | Geo::DependencyProxyBlobRegistry registry class. | | <a id="georegistryclassdependency_proxy_manifest_registry"></a>`DEPENDENCY_PROXY_MANIFEST_REGISTRY` | Geo::DependencyProxyManifestRegistry registry class. | +| <a id="georegistryclassdesign_management_repository_registry"></a>`DESIGN_MANAGEMENT_REPOSITORY_REGISTRY` | Geo::DesignManagementRepositoryRegistry registry class. | | <a id="georegistryclassjob_artifact_registry"></a>`JOB_ARTIFACT_REGISTRY` | Geo::JobArtifactRegistry registry class. | | <a id="georegistryclasslfs_object_registry"></a>`LFS_OBJECT_REGISTRY` | Geo::LfsObjectRegistry registry class. | | <a id="georegistryclassmerge_request_diff_registry"></a>`MERGE_REQUEST_DIFF_REGISTRY` | Geo::MergeRequestDiffRegistry registry class. | diff --git a/doc/development/ai_features.md b/doc/development/ai_features.md index 25da99addc1..f8d19dc26bf 100644 --- a/doc/development/ai_features.md +++ b/doc/development/ai_features.md @@ -66,7 +66,9 @@ All AI features are experimental. 1. Enable **Experiment features** 1. Enable **Third-party AI services** 1. Enable the specific feature flag for the feature you want to test -1. Set either the required access token `OpenAi` or `Vertex`. Ask in [`#ai_enablement_team`](https://gitlab.slack.com/archives/C051K31F30R) to receive an access token. +1. Set the required access token. To receive an access token: + 1. For Vertex, follow the [instructions below](#configure-gcp-vertex-access). + 1. For all other providers, create an access request where `@m_gill`, `@wayne`, and `@timzallmann` are the tech stack owners. ### Set up the embedding database @@ -102,7 +104,7 @@ tail -f log/llm.log In order to obtain a GCP service key for local development, please follow the steps below: -- Create a sandbox GCP environment by visiting [this page](https://about.gitlab.com/handbook/infrastructure-standards/#individual-environment) and following the instructions +- Create a sandbox GCP environment by visiting [this page](https://about.gitlab.com/handbook/infrastructure-standards/#individual-environment) and following the instructions, or by requesting access to our existing group environment by using [this template](https://gitlab.com/gitlab-com/it/infra/issue-tracker/-/issues/new?issuable_template=gcp_group_account_iam_update_request). At this time, access to any endpoints outside of `text-bison` or `chat-bison` must be made through the group environment. - In the GCP console, go to `IAM & Admin` > `Service Accounts` and click on the "Create new service account" button - Name the service account something specific to what you're using it for. Select Create and Continue. Under `Grant this service account access to project`, select the role `Vertex AI User`. Select `Continue` then `Done` - Select your new service account and `Manage keys` > `Add Key` > `Create new key`. This will download the **private** JSON credentials for your service account. Your full settings should then be: diff --git a/doc/development/feature_flags/index.md b/doc/development/feature_flags/index.md index c2026ab8966..4b0ad30e3ad 100644 --- a/doc/development/feature_flags/index.md +++ b/doc/development/feature_flags/index.md @@ -149,7 +149,8 @@ created using the [Experiment Tracking template](https://gitlab.com/gitlab-org/g `worker` feature flags are used for controlling Sidekiq workers behavior, such as deferring Sidekiq jobs. `worker` feature flags likely do not have any YAML definition as the name could be dynamically generated using -the worker name itself, e.g. `defer_sidekiq_jobs:AuthorizedProjectsWorker`. +the worker name itself, e.g. `defer_sidekiq_jobs:AuthorizedProjectsWorker`. Some examples for using `worker` type feature +flags can be found in [deferring Sidekiq jobs](#deferring-sidekiq-jobs). ## Feature flag definition and validation @@ -705,3 +706,40 @@ test is written to enable/disable a feature flag explicitly. When a feature flag is changed on Staging or on GitLab.com, a Slack message will be posted to the `#qa-staging` or `#qa-production` channels to inform the pipeline triage DRI so that they can more easily determine if any failures are related to a feature flag change. However, if you are working on a change you can help to avoid unexpected failures by [confirming that the end-to-end tests pass with a feature flag enabled.](../testing_guide/end_to_end/feature_flags.md#confirming-that-end-to-end-tests-pass-with-a-feature-flag-enabled) + +## Controlling Sidekiq worker behavior with feature flags + +Feature flags with [`worker` type](#worker-type) can be used to control the behavior of a Sidekiq worker. + +### Deferring Sidekiq jobs + +Feature flags with the format of `defer_sidekiq_jobs:{WorkerName}` delay the execution of the worker +by scheduling the job at a later time. +Deferring jobs can be useful during an incident where contentious behavior from +worker instances are saturating infrastructure resources (such as database and database connection pool). +The implementation can be found at [DeferJobs Sidekiq server middleware](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/sidekiq_middleware/defer_jobs.rb). + +NOTE: +Jobs are deferred indefinitely as long as the feature flag is enabled. It is important to disable the +feature flag after the worker is deemed safe to continue processing. + +When set to true, 100% of the jobs are deferred. When you want processing to resume, you can +use a **percentage of time** rollout. For example: + +```shell +# defer 100% of the jobs +/chatops run feature set defer_sidekiq_jobs:SlowRunningWorker true + +# defer 99% of the jobs, only letting 1% processed +/chatops run feature set defer_sidekiq_jobs:SlowRunningWorker 99 + +# defer 50% of the jobs +/chatops run feature set defer_sidekiq_jobs:SlowRunningWorker 50 + +# stop deferring the jobs, jobs are being processed normally +/chatops run feature set defer_sidekiq_jobs:SlowRunningWorker false +``` + +NOTE: +The percentage of time value denotes the percentage of time the jobs are being deferred (instead of being processed). +For example, setting to `99` means only 1% of the jobs are being processed at random. diff --git a/doc/development/sec/analyzer_development_guide.md b/doc/development/sec/analyzer_development_guide.md index 31058427b55..3f1420b8d3d 100644 --- a/doc/development/sec/analyzer_development_guide.md +++ b/doc/development/sec/analyzer_development_guide.md @@ -361,3 +361,9 @@ This issue will guide you through the whole release process. In general, you hav - Max role: `Developer` - Scope of the associated `GITLAB_TOKEN`: - Expiry Date of the associated `GITLAB_TOKEN`: + +#### Dependency updates + +All dependencies and upstream scanners (if any) used in the analyzer source are updated on a monthly cadence which primarily includes security fixes and non-breaking changes. + +- Static Analysis team uses a custom internal tool ([SastBot](https://gitlab.com/gitlab-org/security-products/analyzers/sast-analyzer-deps-bot#dependency-update-automation)) to automate dependency management of all the SAST analyzers. SastBot generates MRs on the **8th of each month** and distributes their assignment among Static Analysis team members to take them forward for review. For details on the process, see [Dependency Update Automation](https://gitlab.com/gitlab-org/security-products/analyzers/sast-analyzer-deps-bot#dependency-update-automation). diff --git a/lib/gitlab/gl_repository.rb b/lib/gitlab/gl_repository.rb index 268d5d3e564..9161a1a138f 100644 --- a/lib/gitlab/gl_repository.rb +++ b/lib/gitlab/gl_repository.rb @@ -34,7 +34,7 @@ module Gitlab DESIGN = ::Gitlab::GlRepository::RepoType.new( name: :design, access_checker_class: ::Gitlab::GitAccessDesign, - repository_resolver: -> (project) { project.design_management_repository.repository }, + repository_resolver: -> (project) { project.find_or_create_design_management_repository.repository }, suffix: :design, container_class: DesignManagement::Repository ).freeze diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml index 36a3c73271b..56cbc5f1bb4 100644 --- a/lib/gitlab/import_export/project/import_export.yml +++ b/lib/gitlab/import_export/project/import_export.yml @@ -121,7 +121,6 @@ tree: - label: - :priorities - :service_desk_setting - - :design_management_repository group_members: - :user diff --git a/lib/gitlab/import_export/project/relation_factory.rb b/lib/gitlab/import_export/project/relation_factory.rb index 5d7e3ea9ed7..8c673acdd1a 100644 --- a/lib/gitlab/import_export/project/relation_factory.rb +++ b/lib/gitlab/import_export/project/relation_factory.rb @@ -23,6 +23,7 @@ module Gitlab design: 'DesignManagement::Design', designs: 'DesignManagement::Design', design_management_repository: 'DesignManagement::Repository', + design_management_repository_state: 'Geo::DesignManagementRepositoryState', design_versions: 'DesignManagement::Version', actions: 'DesignManagement::Action', labels: :project_labels, diff --git a/lib/gitlab/instrumentation/redis_cluster_validator.rb b/lib/gitlab/instrumentation/redis_cluster_validator.rb index 1567e54d8da..7b6200af66c 100644 --- a/lib/gitlab/instrumentation/redis_cluster_validator.rb +++ b/lib/gitlab/instrumentation/redis_cluster_validator.rb @@ -211,6 +211,10 @@ module Gitlab Thread.current[:allow_cross_slot_commands] -= 1 end + def allow_cross_slot_commands? + Thread.current[:allow_cross_slot_commands].to_i > 0 + end + private def extract_keys(command) @@ -226,10 +230,6 @@ module Gitlab keys.map { |key| key_slot(key) }.uniq.many? # rubocop: disable CodeReuse/ActiveRecord end - def allow_cross_slot_commands? - Thread.current[:allow_cross_slot_commands].to_i > 0 - end - def key_slot(key) ::Redis::Cluster::KeySlotConverter.convert(extract_hash_tag(key)) end diff --git a/lib/gitlab/patch/redis_cache_store.rb b/lib/gitlab/patch/redis_cache_store.rb new file mode 100644 index 00000000000..f71262896d1 --- /dev/null +++ b/lib/gitlab/patch/redis_cache_store.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +module Gitlab + module Patch + module RedisCacheStore + PATCH_INPUT_LIMIT = 100 + PIPELINE_BATCH_SIZE = 100 + + # We will try keep patched code explicit and matching the original signature in + # https://github.com/rails/rails/blob/v6.1.7.2/activesupport/lib/active_support/cache/redis_cache_store.rb#L361 + def read_multi_mget(*names) # rubocop:disable Style/ArgumentsForwarding + return super unless enable_rails_cache_pipeline_patch? + return super if names.size > PATCH_INPUT_LIMIT # avoid excessive apdex degradation during benchmarking exercise + + patched_read_multi_mget(*names) # rubocop:disable Style/ArgumentsForwarding + end + + # `delete_multi_entries` in Rails runs a multi-key `del` command + # patch will run pipelined single-key `del` for Redis Cluster compatibility + def delete_multi_entries(entries, **options) + return super unless enable_rails_cache_pipeline_patch? + + delete_count = 0 + redis.with do |conn| + entries.each_slice(PIPELINE_BATCH_SIZE) do |subset| + delete_count += conn.pipelined do |pipeline| + subset.each { |entry| pipeline.del(entry) } + end.sum + end + end + delete_count + end + + # Copied from https://github.com/rails/rails/blob/v6.1.6.1/activesupport/lib/active_support/cache/redis_cache_store.rb + # re-implements `read_multi_mget` using a pipeline of `get`s rather than an `mget` + # + def patched_read_multi_mget(*names) + options = names.extract_options! + options = merged_options(options) + return {} if names == [] + + raw = options&.fetch(:raw, false) + + keys = names.map { |name| normalize_key(name, options) } + + values = failsafe(:patched_read_multi_mget, returning: {}) do + redis.with { |c| pipeline_mget(c, keys) } + end + + names.zip(values).each_with_object({}) do |(name, value), results| + if value # rubocop:disable Style/Next + entry = deserialize_entry(value, raw: raw) + unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(name, options)) + results[name] = entry.value + end + end + end + end + + def pipeline_mget(conn, keys) + conn.pipelined do |p| + keys.each { |key| p.get(key) } + end + end + + private + + def enable_rails_cache_pipeline_patch? + redis_cache? && + Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands? && + ::Feature.enabled?(:enable_rails_cache_pipeline_patch) # rubocop:disable Cop/FeatureFlagUsage + end + + def redis_cache? + # We do not want to risk cycles of feature code calling redis calling feature code. + # Also, we only want to benchmark redis-cache, hence repository-cache and rate-limiting are excluded. + !is_a?(Gitlab::Redis::FeatureFlag::FeatureFlagStore) && + !is_a?(Gitlab::Redis::RepositoryCache::RepositoryCacheStore) && + !is_a?(Gitlab::Redis::RateLimiting::RateLimitingStore) + end + end + end +end diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index 6d576bc8e38..9aa98189f30 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -295,7 +295,6 @@ RSpec.describe MergeRequestsFinder, feature_category: :code_review_workflow do end { - wip: ["WIP:", "wip", "[wip]"], draft: ["Draft:", "Draft -", "[Draft]", "(Draft)"] }.each do |draft_param_key, title_prefixes| title_prefixes.each do |title_prefix| diff --git a/spec/lib/gitlab/gl_repository/repo_type_spec.rb b/spec/lib/gitlab/gl_repository/repo_type_spec.rb index 2ac2fc1fd4b..4345df1b018 100644 --- a/spec/lib/gitlab/gl_repository/repo_type_spec.rb +++ b/spec/lib/gitlab/gl_repository/repo_type_spec.rb @@ -136,7 +136,7 @@ RSpec.describe Gitlab::GlRepository::RepoType do describe Gitlab::GlRepository::DESIGN do it_behaves_like 'a repo type' do let(:expected_repository) { project.design_repository } - let(:expected_container) { project.design_management_repository } + let(:expected_container) { expected_repository.container } let(:expected_id) { expected_container.id } let(:expected_identifier) { "design-#{expected_id}" } let(:expected_suffix) { '.design' } diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 34f9948b9dc..e6829363922 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -798,6 +798,7 @@ project: - analytics_dashboards_configuration_project - analytics_dashboards_pointer - design_management_repository +- design_management_repository_state award_emoji: - awardable - user diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index faf345e8f78..9e5b5ccd64a 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -932,11 +932,6 @@ DesignManagement::Version: - created_at - sha - author_id -DesignManagement::Repository: -- id -- project_id -- created_at -- updated_at ZoomMeeting: - id - project_id diff --git a/spec/lib/gitlab/patch/redis_cache_store_spec.rb b/spec/lib/gitlab/patch/redis_cache_store_spec.rb new file mode 100644 index 00000000000..e1bf774d157 --- /dev/null +++ b/spec/lib/gitlab/patch/redis_cache_store_spec.rb @@ -0,0 +1,177 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Patch::RedisCacheStore, :use_clean_rails_redis_caching, feature_category: :scalability do + before do + Rails.cache.write('x', 1) + Rails.cache.write('y', 2) + Rails.cache.write('z', 3) + + Rails.cache.write('{user1}:x', 1) + Rails.cache.write('{user1}:y', 2) + Rails.cache.write('{user1}:z', 3) + end + + describe '#read_multi_mget' do + it 'runs multi-key command if no cross-slot command is expected' do + Rails.cache.redis.with do |redis| + expect(redis).not_to receive(:pipelined) + end + + expect( + Rails.cache.fetch_multi('{user1}:x', '{user1}:y', '{user1}:z') { |key| key } + ).to eq({ '{user1}:x' => 1, '{user1}:y' => 2, '{user1}:z' => 3 }) + end + + it 'skips patch if input is above 100' do + Rails.cache.redis.with do |redis| + expect(redis).not_to receive(:pipelined) + end + + Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do + Rails.cache.read_multi(*Array.new(101) { |i| i }) + end + end + + shared_examples "fetches multiple keys" do |patched| + it 'reads multiple keys' do + if patched + Rails.cache.redis.with do |redis| + expect(redis).to receive(:pipelined).once.and_call_original + end + end + + expect(::Feature).to receive(:enabled?) + .with(:feature_flag_state_logs, { default_enabled_if_undefined: nil, type: :ops }) + .exactly(:once) + .and_call_original + + expect(::Feature).to receive(:enabled?) + .with(:enable_rails_cache_pipeline_patch) + .exactly(:once) + .and_call_original + + expect( + Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do + # fetch_multi requires a block and we have to specifically test it + # as it is used in the Gitlab project + Rails.cache.fetch_multi('x', 'y', 'z') { |key| key } + end + ).to eq({ 'x' => 1, 'y' => 2, 'z' => 3 }) + end + end + + shared_examples 'reading using non redis cache stores' do |klass| + it 'does not affect non Redis::Cache cache stores' do + klass.cache_store.redis.with do |redis| + expect(redis).not_to receive(:pipelined) + end + + Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do + klass.cache_store.fetch_multi('x', 'y', 'z') { |key| key } + end + end + end + + context 'when reading from non redis-cache stores' do + it_behaves_like 'reading using non redis cache stores', Gitlab::Redis::RepositoryCache + it_behaves_like 'reading using non redis cache stores', Gitlab::Redis::FeatureFlag + it_behaves_like 'reading using non redis cache stores', Gitlab::Redis::RateLimiting + end + + context 'when feature flag is disabled' do + before do + stub_feature_flags(enable_rails_cache_pipeline_patch: false) + end + + it_behaves_like 'fetches multiple keys' + end + + it_behaves_like 'fetches multiple keys', true + end + + describe '#delete_multi_entries' do + shared_examples "deletes multiple keys" do |patched| + it 'deletes multiple keys' do + if patched + Rails.cache.redis.with do |redis| + expect(redis).to receive(:pipelined).once.and_call_original + end + end + + expect(::Feature).to receive(:enabled?) + .with(:feature_flag_state_logs, { default_enabled_if_undefined: nil, type: :ops }) + .exactly(:once) + .and_call_original + + expect(::Feature).to receive(:enabled?) + .with(:enable_rails_cache_pipeline_patch) + .exactly(:once) + .and_call_original + + expect( + Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do + Rails.cache.delete_multi(%w[x y z]) + end + ).to eq(3) + end + end + + shared_examples 'deleting using non redis cache stores' do |klass| + it 'does not affect non Redis::Cache cache stores' do + klass.cache_store.redis.with do |redis| + expect(redis).not_to receive(:pipelined) + end + + Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do + klass.cache_store.delete_multi(%w[x y z]) + end + end + end + + context 'when deleting from non redis-cache stores' do + it_behaves_like 'deleting using non redis cache stores', Gitlab::Redis::RepositoryCache + it_behaves_like 'deleting using non redis cache stores', Gitlab::Redis::FeatureFlag + it_behaves_like 'deleting using non redis cache stores', Gitlab::Redis::RateLimiting + end + + context 'when deleting large amount of keys' do + before do + 200.times { |i| Rails.cache.write(i, i) } + end + + it 'calls pipeline multiple times' do + Rails.cache.redis.with do |redis| + expect(redis).to receive(:pipelined).twice.and_call_original + end + + expect( + Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do + Rails.cache.delete_multi(Array(0..199)) + end + ).to eq(200) + end + end + + it 'runs multi-key command if no cross-slot command is expected' do + Rails.cache.redis.with do |redis| + expect(redis).not_to receive(:pipelined) + end + + expect( + Rails.cache.delete_multi(%w[{user1}:x {user1}:y {user1}:z]) + ).to eq(3) + end + + context 'when feature flag is disabled' do + before do + stub_feature_flags(enable_rails_cache_pipeline_patch: false) + end + + it_behaves_like 'deletes multiple keys' + end + + it_behaves_like 'deletes multiple keys', true + end +end diff --git a/spec/models/clusters/agent_spec.rb b/spec/models/clusters/agent_spec.rb index 10081b955f4..7c546f42d5d 100644 --- a/spec/models/clusters/agent_spec.rb +++ b/spec/models/clusters/agent_spec.rb @@ -13,6 +13,7 @@ RSpec.describe Clusters::Agent, feature_category: :deployment_management do it { is_expected.to have_many(:ci_access_authorized_groups).through(:ci_access_group_authorizations) } it { is_expected.to have_many(:ci_access_project_authorizations).class_name('Clusters::Agents::Authorizations::CiAccess::ProjectAuthorization') } it { is_expected.to have_many(:ci_access_authorized_projects).through(:ci_access_project_authorizations).class_name('::Project') } + it { is_expected.to have_many(:environments).class_name('::Environment') } 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/environment_spec.rb b/spec/models/environment_spec.rb index 87beba680d8..061291906a0 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -18,6 +18,7 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching, feature_categ it { is_expected.to belong_to(:project).required } it { is_expected.to belong_to(:merge_request).optional } + it { is_expected.to belong_to(:cluster_agent).optional } it { is_expected.to have_many(:deployments) } it { is_expected.to have_many(:metrics_dashboard_annotations) } diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb index 240d81bb485..5a5e8691a76 100644 --- a/spec/services/notes/create_service_spec.rb +++ b/spec/services/notes/create_service_spec.rb @@ -482,7 +482,7 @@ RSpec.describe Notes::CreateService, feature_category: :team_planning do expect(noteable.target_branch == "fix").to eq(can_use_quick_action) } ), - # Set WIP status + # Set Draft status QuickAction.new( action_text: "/draft", before_action: -> { diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb index 665f930a0a8..7da23a59a58 100644 --- a/spec/services/projects/destroy_service_spec.rb +++ b/spec/services/projects/destroy_service_spec.rb @@ -113,7 +113,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi destroy_project(project, user, {}) expect(project.reload.delete_error).to be_present - expect(project.delete_error).to include(error_message) + expect(project.delete_error).to match(error_message) end end @@ -287,7 +287,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi .to receive(:remove_legacy_registry_tags).and_return(false) end - it_behaves_like 'handles errors thrown during async destroy', "Failed to remove some tags" + it_behaves_like 'handles errors thrown during async destroy', /Failed to remove some tags/ end context 'when `remove_repository` fails' do @@ -296,7 +296,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi .to receive(:remove_repository).and_return(false) end - it_behaves_like 'handles errors thrown during async destroy', "Failed to remove project repository" + it_behaves_like 'handles errors thrown during async destroy', /Failed to remove/ end context 'when `execute` raises expected error' do @@ -305,7 +305,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi .to receive(:destroy!).and_raise(StandardError.new("Other error message")) end - it_behaves_like 'handles errors thrown during async destroy', "Other error message" + it_behaves_like 'handles errors thrown during async destroy', /Other error message/ end context 'when `execute` raises unexpected error' do @@ -456,6 +456,8 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi end context 'repository removal' do + # 1. Project repository + # 2. Wiki repository it 'removal of existing repos' do expect_next_instances_of(Repositories::DestroyService, 2) do |instance| expect(instance).to receive(:execute).and_return(status: :success) @@ -529,7 +531,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi end end - it_behaves_like 'handles errors thrown during async destroy', "Failed to remove webhooks" + it_behaves_like 'handles errors thrown during async destroy', /Failed to remove webhooks/ end end diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index 48d5935f22f..37adc9e4867 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -716,7 +716,7 @@ RSpec.describe Projects::TransferService, feature_category: :projects do end def clear_design_repo_memoization - project.design_management_repository.clear_memoization(:repository) + project&.design_management_repository&.clear_memoization(:repository) project.clear_memoization(:design_repository) end diff --git a/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb index 507bcd44ee8..14bbe837e31 100644 --- a/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb +++ b/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb @@ -74,7 +74,7 @@ RSpec.shared_context 'MergeRequestsFinder multiple projects with merge requests :merge_request, :simple, author: user, assignees: [user2], reviewers: [user], source_project: project2, target_project: project2, state: 'locked', - title: 'thing WIP thing' + title: 'thing Draft thing' ) end @@ -82,7 +82,7 @@ RSpec.shared_context 'MergeRequestsFinder multiple projects with merge requests create( :merge_request, :simple, author: user, source_project: project3, target_project: project3, - title: 'WIP thing' + title: 'Draft - thing' ) end @@ -90,7 +90,7 @@ RSpec.shared_context 'MergeRequestsFinder multiple projects with merge requests create( :merge_request, :simple, author: user, source_project: project4, target_project: project4, - title: '[WIP]' + title: '[Draft]' ) end diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb index bd1bfc46d53..5c8a75aca3f 100644 --- a/spec/workers/post_receive_spec.rb +++ b/spec/workers/post_receive_spec.rb @@ -491,16 +491,26 @@ RSpec.describe PostReceive, feature_category: :source_code_management do end end - describe 'processing design changes' do - let(:gl_repository) { "design-#{project.id}" } + describe '#process_design_management_repository_changes' do + let(:gl_repository) { "design-#{project.design_management_repository.id}" } - it 'does not do anything' do - worker = described_class.new + before do + project.create_design_management_repository + project.design_management_repository.repository.create_if_not_exists + end - expect(worker).not_to receive(:process_wiki_changes) - expect(worker).not_to receive(:process_project_changes) + it 'does not log an error' do + expect(Gitlab::GitLogger).not_to receive(:error) + expect(Gitlab::GitPostReceive).to receive(:new).and_call_original + expect_next(described_class).to receive(:process_design_management_repository_changes) - described_class.new.perform(gl_repository, key_id, base64_changes) + perform + end + + it 'expires cache' do + expect_next(described_class).to receive(:expire_caches).with(anything, project.design_management_repository.repository) + + perform end it_behaves_like 'an idempotent worker' |