diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index 7022335631b..e9e02761665 100644 --- a/.rubocop_manual_todo.yml +++ b/.rubocop_manual_todo.yml @@ -1685,6 +1685,7 @@ Gitlab/NamespacedClass: - 'app/uploaders/personal_file_uploader.rb' - 'app/validators/abstract_path_validator.rb' - 'app/validators/addressable_url_validator.rb' + - 'app/validators/any_field_validator.rb' - 'app/validators/array_members_validator.rb' - 'app/validators/branch_filter_validator.rb' - 'app/validators/certificate_fingerprint_validator.rb' diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js index 7922ff22a70..e9772232eaf 100644 --- a/app/assets/javascripts/lib/utils/url_utility.js +++ b/app/assets/javascripts/lib/utils/url_utility.js @@ -474,19 +474,17 @@ export function queryToObject(query, { gatherArrays = false, legacySpacesDecode } const decodedValue = legacySpacesDecode ? decodeURIComponent(value) : decodeUrlParameter(value); + const decodedKey = legacySpacesDecode ? decodeURIComponent(key) : decodeUrlParameter(key); - if (gatherArrays && key.endsWith('[]')) { - const decodedKey = legacySpacesDecode - ? decodeURIComponent(key.slice(0, -2)) - : decodeUrlParameter(key.slice(0, -2)); + if (gatherArrays && decodedKey.endsWith('[]')) { + const decodedArrayKey = decodedKey.slice(0, -2); - if (!Array.isArray(accumulator[decodedKey])) { - accumulator[decodedKey] = []; + if (!Array.isArray(accumulator[decodedArrayKey])) { + accumulator[decodedArrayKey] = []; } - accumulator[decodedKey].push(decodedValue); - } else { - const decodedKey = legacySpacesDecode ? decodeURIComponent(key) : decodeUrlParameter(key); + accumulator[decodedArrayKey].push(decodedValue); + } else { accumulator[decodedKey] = decodedValue; } diff --git a/app/models/concerns/any_field_validation.rb b/app/models/concerns/any_field_validation.rb deleted file mode 100644 index 987c4e7800e..00000000000 --- a/app/models/concerns/any_field_validation.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -# This module enables a record to be valid if any field is present -# -# Overwrite one_of_required_fields to set one of which fields must be present -module AnyFieldValidation - extend ActiveSupport::Concern - - included do - validate :any_field_present - end - - private - - def any_field_present - return unless one_of_required_fields.all? { |field| self[field].blank? } - - errors.add(:base, _("At least one field of %{one_of_required_fields} must be present") % - { one_of_required_fields: one_of_required_fields }) - end - - def one_of_required_fields - raise NotImplementedError - end -end diff --git a/app/validators/any_field_validator.rb b/app/validators/any_field_validator.rb new file mode 100644 index 00000000000..b5d01c65585 --- /dev/null +++ b/app/validators/any_field_validator.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +# AnyFieldValidator +# +# Custom validator that checks if any of the provided +# fields are present to ensure creation of a non-empty +# record +# +# Example: +# +# class MyModel < ApplicationRecord +# validates_with AnyFieldValidator, fields: %w[type name url] +# end +class AnyFieldValidator < ActiveModel::Validator + def initialize(*args) + super + + if options[:fields].blank? + raise 'Provide the fields options' + end + end + + def validate(record) + return unless one_of_required_fields.all? { |field| record[field].blank? } + + record.errors.add(:base, _("At least one field of %{one_of_required_fields} must be present") % + { one_of_required_fields: one_of_required_fields }) + end + + private + + def one_of_required_fields + options[:fields] + end +end diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 0348d5ed879..ba2d6aa79eb 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -28,5 +28,7 @@ = yield :before_content = yield = yield :after_content + -# This is needed by [GitLab JH](https://gitlab.com/gitlab-jh/jh-team/gitlab-cn/-/issues/81) + = render_if_exists "shared/footer/global_footer" = render "layouts/nav/top_nav_responsive", class: 'layout-page content-wrapper-margin' diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index 530d7342d57..204d4d88f0a 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -27,5 +27,5 @@ Gitlab::Application.config.session_store( secure: Gitlab.config.gitlab.https, httponly: true, expires_in: Settings.gitlab['session_expire_delay'] * 60, - path: Rails.application.config.relative_url_root.nil? ? '/' : Gitlab::Application.config.relative_url_root + path: Rails.application.config.relative_url_root.presence || '/' ) diff --git a/doc/administration/geo/replication/multiple_servers.md b/doc/administration/geo/replication/multiple_servers.md index 182b0408f9c..027e8933032 100644 --- a/doc/administration/geo/replication/multiple_servers.md +++ b/doc/administration/geo/replication/multiple_servers.md @@ -17,69 +17,59 @@ described, it is possible to adapt these instructions to your needs. _[diagram source - GitLab employees only](https://docs.google.com/drawings/d/1z0VlizKiLNXVVVaERFwgsIOuEgjcUqDTWPdQYsE7Z4c/edit)_ -The topology above assumes the **primary** and **secondary** Geo clusters +The topology above assumes the **primary** and **secondary** Geo sites are located in two separate locations, on their own virtual network with private IP addresses. The network is configured such that all machines in one geographic location can communicate with each other using their private IP addresses. The IP addresses given are examples and may be different depending on the network topology of your deployment. -The only external way to access the two Geo deployments is by HTTPS at +The only external way to access the two Geo sites is by HTTPS at `gitlab.us.example.com` and `gitlab.eu.example.com` in the example above. NOTE: -The **primary** and **secondary** Geo deployments must be able to communicate to each other over HTTPS. +The **primary** and **secondary** Geo sites must be able to communicate to each other over HTTPS. ## Redis and PostgreSQL for multiple nodes -Geo supports: - -- Redis and PostgreSQL on the **primary** node configured for multiple nodes. -- Redis on **secondary** nodes configured for multiple nodes. - -NOTE: -Support for PostgreSQL on **secondary** nodes in multi-node configuration -[is planned](https://gitlab.com/groups/gitlab-org/-/epics/2536). - Because of the additional complexity involved in setting up this configuration for PostgreSQL and Redis, it is not covered by this Geo multi-node documentation. For more information on setting up a multi-node PostgreSQL cluster and Redis cluster using the Omnibus GitLab package, see: -- [PostgreSQL multi-node documentation](../../postgresql/replication_and_failover.md) +- [Geo multi-node database replication](../setup/database.md#multi-node-database-replication) - [Redis multi-node documentation](../../redis/replication_and_failover.md) NOTE: It is possible to use cloud hosted services for PostgreSQL and Redis, but this is beyond the scope of this document. -## Prerequisites: Two working GitLab multi-node clusters +## Prerequisites: Two independently working GitLab multi-node sites -One cluster serves as the **primary** node. Use the -[GitLab multi-node documentation](../../reference_architectures/index.md) to set this up. If +One GitLab site serves as the Geo **primary** site. Use the +[GitLab reference architectures documentation](../../reference_architectures/index.md) +to set this up. You can use different reference architecture sizes for each Geo site. If you already have a working GitLab instance that is in-use, it can be used as a -**primary**. +**primary** site. -The second cluster serves as the **secondary** node. Again, use the -[GitLab multi-node documentation](../../reference_architectures/index.md) to set this up. +The second GitLab site serves as the Geo **secondary** site. Again, use the +[GitLab reference architectures documentation](../../reference_architectures/index.md) to set this up. It's a good idea to log in and test it. However, be aware that its data is -wiped out as part of the process of replicating from the **primary** node. +wiped out as part of the process of replicating from the **primary** site. -## Configure the GitLab cluster to be the **primary** node +## Configure a GitLab site to be the Geo **primary** site -The following steps enable a GitLab cluster to serve as the **primary** node. +The following steps enable a GitLab site to serve as the Geo **primary** site. -### Step 1: Configure the **primary** frontend servers +### Step 1: Configure the **primary** frontend nodes 1. Edit `/etc/gitlab/gitlab.rb` and add the following: ```ruby ## - ## Enable the Geo primary role - ## - roles ['geo_primary_role'] - - ## - ## The unique identifier for the Geo node. + ## The unique identifier for the Geo site. It's recommended to use a + ## physical location as a name, for example "us-east", instead of + ## "secondary" or "geo". It's case-sensitive, and most characters are + ## allowed. It should be the same for all nodes in a Geo site. ## gitlab_rails['geo_node_name'] = '' @@ -93,150 +83,58 @@ After making these changes, [reconfigure GitLab](../../restart_gitlab.md#omnibus NOTE: PostgreSQL and Redis should have already been disabled on the -application servers during normal GitLab multi-node setup. Connections -from the application servers to services on the backend servers should +application nodes during normal GitLab multi-node setup. Connections +from the application nodes to services on the backend nodes should have also been configured. See multi-node configuration documentation for [PostgreSQL](../../postgresql/replication_and_failover.md#configuring-the-application-nodes) and [Redis](../../redis/replication_and_failover.md#example-configuration-for-the-gitlab-application). -### Step 2: Configure the **primary** database +## Configure the other GitLab site to be a Geo **secondary** site -1. Edit `/etc/gitlab/gitlab.rb` and add the following: - - ```ruby - ## - ## Configure the Geo primary role and the PostgreSQL role - ## - roles ['geo_primary_role', 'postgres_role'] - ``` - -## Configure a **secondary** node - -A **secondary** cluster is similar to any other GitLab multi-node cluster, with two +A **secondary** site is similar to any other GitLab multi-node site, with three major differences: -- The main PostgreSQL database is a read-only replica of the **primary** node's +- The main PostgreSQL database is a read-only replica of the Geo **primary** site's PostgreSQL database. -- There is also a single PostgreSQL database for the **secondary** cluster, - called the "tracking database", which tracks the synchronization state of - various resources. +- There is an additional PostgreSQL database for each Geo **secondary** site, + called the "Geo tracking database", which tracks the replication and verification + state of various resources. +- There is an additional GitLab service [`geo-logcursor`](../index.md#geo-log-cursor) Therefore, we set up the multi-node components one by one and include deviations from the normal multi-node setup. However, we highly recommend configuring a -brand-new cluster first, as if it were not part of a Geo setup. This allows -verifying that it is a working cluster. And only then should it be modified -for use as a Geo **secondary**. This helps to separate Geo setup problems from -unrelated problems. +brand-new GitLab site first, as if it were not part of a Geo setup. This allows +verifying that it is a working GitLab site. And only then should it be modified +for use as a Geo **secondary** site. This helps to separate Geo setup problems from +unrelated multi-node configuration problems. -### Step 1: Configure the Redis and Gitaly services on the **secondary** node +### Step 1: Configure the Redis and Gitaly services on the Geo **secondary** site Configure the following services, again using the non-Geo multi-node documentation: - [Configuring Redis for GitLab](../../redis/replication_and_failover.md#example-configuration-for-the-gitlab-application) for multiple nodes. - [Gitaly](../../gitaly/index.md), which stores data that is - synchronized from the **primary** node. + synchronized from the Geo **primary** site. NOTE: [NFS](../../nfs.md) can be used in place of Gitaly but is not recommended. -### Step 2: Configure the main read-only replica PostgreSQL database on the **secondary** node +### Step 2: Configure Postgres streaming replication -NOTE: -The following documentation assumes the database runs on -a single node only. Multi-node PostgreSQL on **secondary** nodes is -[not currently supported](https://gitlab.com/groups/gitlab-org/-/epics/2536). - -Configure the [**secondary** database](../setup/database.md) as a read-only replica of -the **primary** database. Use the following as a guide. - -1. Generate an MD5 hash of the desired password for the database user that the - GitLab application uses to access the read-replica database: - - The username (`gitlab` by default) is incorporated into the hash. - - ```shell - gitlab-ctl pg-password-md5 gitlab - # Enter password: - # Confirm password: - # fca0b89a972d69f00eb3ec98a5838484 - ``` - - Use this hash to fill in `` in the next step. - -1. Edit `/etc/gitlab/gitlab.rb` in the replica database machine, and add the - following: - - ```ruby - ## - ## Configure the Geo secondary role and the PostgreSQL role - ## - roles ['geo_secondary_role', 'postgres_role'] - - ## - ## The unique identifier for the Geo node. - ## This should match the secondary's application node. - ## - gitlab_rails['geo_node_name'] = '' - - ## - ## Secondary address - ## - replace '' with the public or VPC address of your Geo secondary node - ## - replace '' with the public or VPC address of your Geo tracking database node - ## - postgresql['listen_address'] = '' - postgresql['md5_auth_cidr_addresses'] = ['/32', '/32'] - - ## - ## Database credentials password (defined previously in primary node) - ## - replicate same values here as defined in primary node - ## - postgresql['sql_user_password'] = '' - gitlab_rails['db_password'] = '' - - ## - ## When running the Geo tracking database on a separate machine, disable it - ## here and allow connections from the tracking database host. And ensure - ## the tracking database IP is in postgresql['md5_auth_cidr_addresses'] above. - ## - geo_postgresql['enable'] = false - - ## - ## Disable all other services that aren't needed. We had to enable - ## geo_secondary_role to cause some configuration changes to postgresql, but - ## the role enables single-node services by default. - ## - alertmanager['enable'] = false - consul['enable'] = false - geo_logcursor['enable'] = false - gitaly['enable'] = false - gitlab_exporter['enable'] = false - gitlab_workhorse['enable'] = false - nginx['enable'] = false - node_exporter['enable'] = false - pgbouncer_exporter['enable'] = false - prometheus['enable'] = false - redis['enable'] = false - redis_exporter['enable'] = false - patroni['enable'] = false - sidekiq['enable'] = false - sidekiq_cluster['enable'] = false - puma['enable'] = false - ``` - -After making these changes, [reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure) so the changes take effect. +Follow the [Geo database replication instructions](../setup/database.md). If using an external PostgreSQL instance, refer also to [Geo with external PostgreSQL instances](../setup/external_database.md). -### Step 3: Configure the tracking database on the **secondary** node +### Step 3: Configure the Geo tracking database on the Geo **secondary** site -NOTE: -This documentation assumes the tracking database runs on -only a single machine, rather than as a PostgreSQL cluster. +If you want to run the Geo tracking database in a multi-node PostgreSQL cluster, +then follow [Configuring Patroni cluster for the tracking PostgreSQL database](../setup/database.md#configuring-patroni-cluster-for-the-tracking-postgresql-database). -Configure the tracking database. +If you want to run the Geo tracking database on a single node, then follow +the instructions below. 1. Generate an MD5 hash of the desired password for the database user that the GitLab application uses to access the tracking database: @@ -254,8 +152,8 @@ Configure the tracking database. Use this hash to fill in `` in the next step. -1. Edit `/etc/gitlab/gitlab.rb` in the tracking database machine, and add the - following: +1. On the machine where the Geo tracking database is intended to run, add the + following to `/etc/gitlab/gitlab.rb`: ```ruby ## @@ -271,29 +169,8 @@ Configure the tracking database. geo_postgresql['md5_auth_cidr_addresses'] = ['/32'] gitlab_rails['db_host'] = '' - # Prevent reconfigure from attempting to run migrations on the replica DB + # Prevent reconfigure from attempting to run migrations on the replica database gitlab_rails['auto_migrate'] = false - - ## - ## Ensure unnecessary services are disabled - ## - alertmanager['enable'] = false - consul['enable'] = false - geo_logcursor['enable'] = false - gitaly['enable'] = false - gitlab_exporter['enable'] = false - gitlab_workhorse['enable'] = false - nginx['enable'] = false - node_exporter['enable'] = false - pgbouncer_exporter['enable'] = false - postgresql['enable'] = false - prometheus['enable'] = false - redis['enable'] = false - redis_exporter['enable'] = false - patroni['enable'] = false - sidekiq['enable'] = false - sidekiq_cluster['enable'] = false - puma['enable'] = false ``` After making these changes, [reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure) so the changes take effect. @@ -301,27 +178,42 @@ After making these changes, [reconfigure GitLab](../../restart_gitlab.md#omnibus If using an external PostgreSQL instance, refer also to [Geo with external PostgreSQL instances](../setup/external_database.md). -### Step 4: Configure the frontend application servers on the **secondary** node +### Step 4: Configure the frontend application nodes on the Geo **secondary** site -In the architecture overview, there are two machines running the GitLab -application services. These services are enabled selectively in the -configuration. +In the minimal [architecture diagram](#architecture-overview) above, there are two +machines running the GitLab application services. These services are enabled +selectively in the configuration. -Configure the GitLab Rails application servers following the relevant steps +Configure the GitLab Rails application nodes following the relevant steps outlined in the [reference architectures](../../reference_architectures/index.md), then make the following modifications: -1. Edit `/etc/gitlab/gitlab.rb` on each application server in the **secondary** - cluster, and add the following: +1. Edit `/etc/gitlab/gitlab.rb` on each application node in the Geo **secondary** + site, and add the following: ```ruby ## - ## Enable the Geo secondary role + ## Enable GitLab application services. The application_role enables many services. + ## Alternatively, you can choose to enable or disable specific services on + ## different nodes to aid in horizontal scaling and separation of concerns. ## - roles ['geo_secondary_role', 'application_role'] + roles ['application_role'] + + ## `application_role` already enables this. You only need this line if + ## you selectively enable individual services that depend on Rails, like + ## `puma`, `sidekiq`, `geo-logcursor`, etc. + gitlab_rails['enable'] = true ## - ## The unique identifier for the Geo node. + ## Enable Geo Log Cursor service + ## + geo_logcursor['enable'] = true + + ## + ## The unique identifier for the Geo site. It's recommended to use a + ## physical location as a name, for example "eu-west", instead of + ## "secondary" or "geo". It's case-sensitive, and most characters are + ## allowed. It should be the same for all nodes in a Geo site. ## gitlab_rails['geo_node_name'] = '' @@ -331,12 +223,11 @@ then make the following modifications: gitlab_rails['auto_migrate'] = false ## - ## Configure the connection to the tracking DB. And disable application - ## servers from running tracking databases. + ## Configure the connection to the tracking database ## + geo_secondary['enable'] = true geo_secondary['db_host'] = '' geo_secondary['db_password'] = '' - geo_postgresql['enable'] = false ## ## Configure connection to the streaming replica database, if you haven't @@ -353,7 +244,7 @@ then make the following modifications: ## ## If you are using custom users not managed by Omnibus, you need to specify - ## UIDs and GIDs like below, and ensure they match between servers in a + ## UIDs and GIDs like below, and ensure they match between nodes in a ## cluster to avoid permissions issues ## user['uid'] = 9000 @@ -369,14 +260,17 @@ If you had set up PostgreSQL cluster using the omnibus package and had set `postgresql['sql_user_password'] = 'md5 digest of secret'`, keep in mind that `gitlab_rails['db_password']` and `geo_secondary['db_password']` contains the plaintext passwords. This is used to let the Rails -servers connect to the databases. +nodes connect to the databases. NOTE: -Make sure that current node IP is listed in `postgresql['md5_auth_cidr_addresses']` setting of your remote database. +Make sure that current node's IP is listed in +`postgresql['md5_auth_cidr_addresses']` setting of the read-replica database to +allow Rails on this node to connect to Postgres. After making these changes [Reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure) so the changes take effect. -On the secondary the following GitLab frontend services are enabled: +In the [architecture overview](#architecture-overview) topology, the following GitLab +services are enabled on the "frontend" nodes: - `geo-logcursor` - `gitlab-pages` @@ -388,60 +282,41 @@ On the secondary the following GitLab frontend services are enabled: - `sidekiq` - `puma` -Verify these services by running `sudo gitlab-ctl status` on the frontend -application servers. +Verify these services exist by running `sudo gitlab-ctl status` on the frontend +application nodes. -### Step 5: Set up the LoadBalancer for the **secondary** node +### Step 5: Set up the LoadBalancer for the Geo **secondary** site -In this topology, a load balancer is required at each geographic location to -route traffic to the application servers. +The minimal [architecture diagram](#architecture-overview) above shows a load +balancer at each geographic location to route traffic to the application nodes. See [Load Balancer for GitLab with multiple nodes](../../load_balancer.md) for more information. -### Step 6: Configure the backend application servers on the **secondary** node +### Step 6: Configure the backend application nodes on the Geo **secondary** site -The minimal reference architecture diagram above shows all application services +The minimal [architecture diagram](#architecture-overview) above shows all application services running together on the same machines. However, for multiple nodes we [strongly recommend running all services separately](../../reference_architectures/index.md). -For example, a Sidekiq server could be configured similarly to the frontend -application servers above, with some changes to run only the `sidekiq` service: +For example, a Sidekiq node could be configured similarly to the frontend +application nodes above, with some changes to run only the `sidekiq` service: -1. Edit `/etc/gitlab/gitlab.rb` on each Sidekiq server in the **secondary** - cluster, and add the following: +1. Edit `/etc/gitlab/gitlab.rb` on each Sidekiq node in the Geo **secondary** + site, and add the following: ```ruby - ## - ## Enable the Geo secondary role - ## - roles ['geo_secondary_role'] - ## ## Enable the Sidekiq service ## sidekiq['enable'] = true + gitlab_rails['enable'] = true ## - ## Ensure unnecessary services are disabled - ## - alertmanager['enable'] = false - consul['enable'] = false - gitaly['enable'] = false - gitlab_exporter['enable'] = false - gitlab_workhorse['enable'] = false - nginx['enable'] = false - node_exporter['enable'] = false - pgbouncer_exporter['enable'] = false - postgresql['enable'] = false - prometheus['enable'] = false - redis['enable'] = false - redis_exporter['enable'] = false - patroni['enable'] = false - puma['enable'] = false - - ## - ## The unique identifier for the Geo node. + ## The unique identifier for the Geo site. It's recommended to use a + ## physical location as a name, for example "eu-west", instead of + ## "secondary" or "geo". It's case-sensitive, and most characters are + ## allowed. It should be the same for all nodes in a Geo site. ## gitlab_rails['geo_node_name'] = '' @@ -451,12 +326,11 @@ application servers above, with some changes to run only the `sidekiq` service: gitlab_rails['auto_migrate'] = false ## - ## Configure the connection to the tracking DB. And disable application - ## servers from running tracking databases. + ## Configure the connection to the tracking database ## + geo_secondary['enable'] = true geo_secondary['db_host'] = '' geo_secondary['db_password'] = '' - geo_postgresql['enable'] = false ## ## Configure connection to the streaming replica database, if you haven't @@ -473,7 +347,7 @@ application servers above, with some changes to run only the `sidekiq` service: ## ## If you are using custom users not managed by Omnibus, you need to specify - ## UIDs and GIDs like below, and ensure they match between servers in a + ## UIDs and GIDs like below, and ensure they match between nodes in a ## cluster to avoid permissions issues ## user['uid'] = 9000 @@ -484,8 +358,8 @@ application servers above, with some changes to run only the `sidekiq` service: registry['gid'] = 9002 ``` - You can similarly configure a server to run only the `geo-logcursor` service + You can similarly configure a node to run only the `geo-logcursor` service with `geo_logcursor['enable'] = true` and disabling Sidekiq with `sidekiq['enable'] = false`. - These servers do not need to be attached to the load balancer. + These nodes do not need to be attached to the load balancer. diff --git a/doc/administration/geo/setup/database.md b/doc/administration/geo/setup/database.md index 706d1382900..b4742775859 100644 --- a/doc/administration/geo/setup/database.md +++ b/doc/administration/geo/setup/database.md @@ -207,7 +207,12 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o ```ruby ## ## Geo Primary role - ## - configure dependent flags automatically to enable Geo + ## - Configures Postgres settings for replication + ## - Prevents automatic upgrade of Postgres since it requires downtime of + ## streaming replication to Geo secondary sites + ## - Enables standard single-node GitLab services like NGINX, Puma, Redis, + ## Sidekiq, etc. If you are segregating services, then you will need to + ## explicitly disable unwanted services. ## roles(['geo_primary_role']) diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md index de5913cb777..426a0bac00c 100644 --- a/doc/administration/reference_architectures/10k_users.md +++ b/doc/administration/reference_architectures/10k_users.md @@ -1,5 +1,4 @@ --- -reading_time: true stage: Enablement group: Distribution info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments diff --git a/doc/administration/reference_architectures/25k_users.md b/doc/administration/reference_architectures/25k_users.md index f04acdba941..f933be78b46 100644 --- a/doc/administration/reference_architectures/25k_users.md +++ b/doc/administration/reference_architectures/25k_users.md @@ -1,5 +1,4 @@ --- -reading_time: true stage: Enablement group: Distribution info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments diff --git a/doc/administration/reference_architectures/2k_users.md b/doc/administration/reference_architectures/2k_users.md index 621033f7eac..7295baf07eb 100644 --- a/doc/administration/reference_architectures/2k_users.md +++ b/doc/administration/reference_architectures/2k_users.md @@ -1,5 +1,4 @@ --- -reading_time: true stage: Enablement group: Distribution info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md index 2bc60473c44..7b161319733 100644 --- a/doc/administration/reference_architectures/3k_users.md +++ b/doc/administration/reference_architectures/3k_users.md @@ -1,5 +1,4 @@ --- -reading_time: true stage: Enablement group: Distribution info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md index 650b49279ea..0f953ae5c02 100644 --- a/doc/administration/reference_architectures/50k_users.md +++ b/doc/administration/reference_architectures/50k_users.md @@ -1,5 +1,4 @@ --- -reading_time: true stage: Enablement group: Distribution info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md index c8e91bc110f..44301cacc12 100644 --- a/doc/administration/reference_architectures/5k_users.md +++ b/doc/administration/reference_architectures/5k_users.md @@ -1,5 +1,4 @@ --- -reading_time: true stage: Enablement group: Distribution info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md index 91215ca21b4..c549024ab86 100644 --- a/doc/development/documentation/index.md +++ b/doc/development/documentation/index.md @@ -152,12 +152,7 @@ comments: false Each page can have additional, optional metadata (set in the [default.html](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/fc3577921343173d589dfa43d837b4307e4e620f/layouts/default.html#L30-52) -Nanoc layout), which is displayed at the top of the page if defined: - -- `reading_time`: If you want to add an indication of the approximate reading - time of a page, you can set `reading_time` to `true`. This uses a simple - [algorithm](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/main/lib/helpers/reading_time.rb) - to calculate the reading time based on the number of words. +Nanoc layout), which is displayed at the top of the page if defined. ## Move or rename a page @@ -209,8 +204,7 @@ To add a redirect: bundle exec rake "gitlab:docs:redirect[doc/user/search/old_file.md, https://example.com]" ``` - Alternatively, you can omit the arguments, and you'll be asked to enter - their values: + Alternatively, you can omit the arguments and be asked to enter their values: ```shell bundle exec rake gitlab:docs:redirect diff --git a/lib/api/concerns/packages/debian_package_endpoints.rb b/lib/api/concerns/packages/debian_package_endpoints.rb index e1a14c37d8c..0acc015f366 100644 --- a/lib/api/concerns/packages/debian_package_endpoints.rb +++ b/lib/api/concerns/packages/debian_package_endpoints.rb @@ -71,7 +71,7 @@ module API route_setting :authentication, authenticate_non_public: true get 'Release.gpg' do - not_found! + distribution_from!(project_or_group).file_signature end # GET {projects|groups}/:id/packages/debian/dists/*distribution/Release @@ -91,8 +91,7 @@ module API route_setting :authentication, authenticate_non_public: true get 'InRelease' do - # Signature to be added in 7.3 of https://gitlab.com/groups/gitlab-org/-/epics/6057#note_582697034 - present_carrierwave_file!(distribution_from!(project_or_group).file) + present_carrierwave_file!(distribution_from!(project_or_group).signed_file) end params do diff --git a/spec/factories/packages/debian/distribution.rb b/spec/factories/packages/debian/distribution.rb index 619308e4e18..2142dba974b 100644 --- a/spec/factories/packages/debian/distribution.rb +++ b/spec/factories/packages/debian/distribution.rb @@ -13,8 +13,18 @@ FactoryBot.define do end trait(:with_file) do + file_signature do + <<~EOF + -----BEGIN PGP SIGNATURE----- + + ABC + -----BEGIN PGP SIGNATURE----- + EOF + end + after(:build) do |distribution, evaluator| distribution.file = fixture_file_upload('spec/fixtures/packages/debian/distribution/Release') + distribution.signed_file = fixture_file_upload('spec/fixtures/packages/debian/distribution/InRelease') end end diff --git a/spec/fixtures/packages/debian/distribution/InRelease b/spec/fixtures/packages/debian/distribution/InRelease new file mode 100644 index 00000000000..49eb6156e39 --- /dev/null +++ b/spec/fixtures/packages/debian/distribution/InRelease @@ -0,0 +1,8 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA256 + +Codename: fixture-distribution +-----BEGIN PGP SIGNATURE----- + +ABC +-----BEGIN PGP SIGNATURE----- diff --git a/spec/frontend/lib/utils/url_utility_spec.js b/spec/frontend/lib/utils/url_utility_spec.js index 3e02b9b3066..c8ac7ffc9d9 100644 --- a/spec/frontend/lib/utils/url_utility_spec.js +++ b/spec/frontend/lib/utils/url_utility_spec.js @@ -670,21 +670,23 @@ describe('URL utility', () => { describe('queryToObject', () => { it.each` - case | query | options | result - ${'converts query'} | ${'?one=1&two=2'} | ${undefined} | ${{ one: '1', two: '2' }} - ${'converts query without ?'} | ${'one=1&two=2'} | ${undefined} | ${{ one: '1', two: '2' }} - ${'removes undefined values'} | ${'?one=1&two=2&three'} | ${undefined} | ${{ one: '1', two: '2' }} - ${'overwrites values with same key and does not change key'} | ${'?one[]=1&one[]=2&two=2&two=3'} | ${undefined} | ${{ 'one[]': '2', two: '3' }} - ${'gathers values with the same array-key, strips `[]` from key'} | ${'?one[]=1&one[]=2&two=2&two=3'} | ${{ gatherArrays: true }} | ${{ one: ['1', '2'], two: '3' }} - ${'overwrites values with the same array-key name'} | ${'?one=1&one[]=2&two=2&two=3'} | ${{ gatherArrays: true }} | ${{ one: ['2'], two: '3' }} - ${'overwrites values with the same key name'} | ${'?one[]=1&one=2&two=2&two=3'} | ${{ gatherArrays: true }} | ${{ one: '2', two: '3' }} - ${'ignores plus symbols'} | ${'?search=a+b'} | ${{ legacySpacesDecode: true }} | ${{ search: 'a+b' }} - ${'ignores plus symbols in keys'} | ${'?search+term=a'} | ${{ legacySpacesDecode: true }} | ${{ 'search+term': 'a' }} - ${'ignores plus symbols when gathering arrays'} | ${'?search[]=a+b'} | ${{ gatherArrays: true, legacySpacesDecode: true }} | ${{ search: ['a+b'] }} - ${'replaces plus symbols with spaces'} | ${'?search=a+b'} | ${undefined} | ${{ search: 'a b' }} - ${'replaces plus symbols in keys with spaces'} | ${'?search+term=a'} | ${undefined} | ${{ 'search term': 'a' }} - ${'replaces plus symbols when gathering arrays'} | ${'?search[]=a+b'} | ${{ gatherArrays: true }} | ${{ search: ['a b'] }} - ${'replaces plus symbols when gathering arrays for values with same key'} | ${'?search[]=a+b&search[]=c+d'} | ${{ gatherArrays: true }} | ${{ search: ['a b', 'c d'] }} + case | query | options | result + ${'converts query'} | ${'?one=1&two=2'} | ${undefined} | ${{ one: '1', two: '2' }} + ${'converts query without ?'} | ${'one=1&two=2'} | ${undefined} | ${{ one: '1', two: '2' }} + ${'removes undefined values'} | ${'?one=1&two=2&three'} | ${undefined} | ${{ one: '1', two: '2' }} + ${'overwrites values with same key and does not change key'} | ${'?one[]=1&one[]=2&two=2&two=3'} | ${undefined} | ${{ 'one[]': '2', two: '3' }} + ${'gathers values with the same array-key, strips `[]` from key'} | ${'?one[]=1&one[]=2&two=2&two=3'} | ${{ gatherArrays: true }} | ${{ one: ['1', '2'], two: '3' }} + ${'overwrites values with the same array-key name'} | ${'?one=1&one[]=2&two=2&two=3'} | ${{ gatherArrays: true }} | ${{ one: ['2'], two: '3' }} + ${'overwrites values with the same key name'} | ${'?one[]=1&one=2&two=2&two=3'} | ${{ gatherArrays: true }} | ${{ one: '2', two: '3' }} + ${'ignores plus symbols'} | ${'?search=a+b'} | ${{ legacySpacesDecode: true }} | ${{ search: 'a+b' }} + ${'ignores plus symbols in keys'} | ${'?search+term=a'} | ${{ legacySpacesDecode: true }} | ${{ 'search+term': 'a' }} + ${'ignores plus symbols when gathering arrays'} | ${'?search[]=a+b'} | ${{ gatherArrays: true, legacySpacesDecode: true }} | ${{ search: ['a+b'] }} + ${'replaces plus symbols with spaces'} | ${'?search=a+b'} | ${undefined} | ${{ search: 'a b' }} + ${'replaces plus symbols in keys with spaces'} | ${'?search+term=a'} | ${undefined} | ${{ 'search term': 'a' }} + ${'preserves square brackets in array params'} | ${'?search[]=a&search[]=b'} | ${{ gatherArrays: true }} | ${{ search: ['a', 'b'] }} + ${'decodes encoded square brackets in array params'} | ${'?search%5B%5D=a&search%5B%5D=b'} | ${{ gatherArrays: true }} | ${{ search: ['a', 'b'] }} + ${'replaces plus symbols when gathering arrays'} | ${'?search[]=a+b'} | ${{ gatherArrays: true }} | ${{ search: ['a b'] }} + ${'replaces plus symbols when gathering arrays for values with same key'} | ${'?search[]=a+b&search[]=c+d'} | ${{ gatherArrays: true }} | ${{ search: ['a b', 'c d'] }} `('$case', ({ query, options, result }) => { expect(urlUtils.queryToObject(query, options)).toEqual(result); }); diff --git a/spec/requests/api/debian_group_packages_spec.rb b/spec/requests/api/debian_group_packages_spec.rb index ddfc94f25db..3e11b480860 100644 --- a/spec/requests/api/debian_group_packages_spec.rb +++ b/spec/requests/api/debian_group_packages_spec.rb @@ -15,7 +15,7 @@ RSpec.describe API::DebianGroupPackages do describe 'GET groups/:id/-/packages/debian/dists/*distribution/Release.gpg' do let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/Release.gpg" } - it_behaves_like 'Debian repository read endpoint', 'GET request', :not_found + it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^-----BEGIN PGP SIGNATURE-----/ end describe 'GET groups/:id/-/packages/debian/dists/*distribution/Release' do @@ -27,7 +27,7 @@ RSpec.describe API::DebianGroupPackages do describe 'GET groups/:id/-/packages/debian/dists/*distribution/InRelease' do let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/InRelease" } - it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^Codename: fixture-distribution\n$/ + it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^-----BEGIN PGP SIGNED MESSAGE-----/ end describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/binary-:architecture/Packages' do diff --git a/spec/requests/api/debian_project_packages_spec.rb b/spec/requests/api/debian_project_packages_spec.rb index d6f22dfbaa3..d0b0debaf13 100644 --- a/spec/requests/api/debian_project_packages_spec.rb +++ b/spec/requests/api/debian_project_packages_spec.rb @@ -15,7 +15,7 @@ RSpec.describe API::DebianProjectPackages do describe 'GET projects/:id/packages/debian/dists/*distribution/Release.gpg' do let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/Release.gpg" } - it_behaves_like 'Debian repository read endpoint', 'GET request', :not_found + it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^-----BEGIN PGP SIGNATURE-----/ end describe 'GET projects/:id/packages/debian/dists/*distribution/Release' do @@ -27,7 +27,7 @@ RSpec.describe API::DebianProjectPackages do describe 'GET projects/:id/packages/debian/dists/*distribution/InRelease' do let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/InRelease" } - it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^Codename: fixture-distribution\n$/ + it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^-----BEGIN PGP SIGNED MESSAGE-----/ end describe 'GET projects/:id/packages/debian/dists/*distribution/:component/binary-:architecture/Packages' do diff --git a/spec/validators/any_field_validator_spec.rb b/spec/validators/any_field_validator_spec.rb new file mode 100644 index 00000000000..bede006abf6 --- /dev/null +++ b/spec/validators/any_field_validator_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe AnyFieldValidator do + context 'when validation is instantiated correctly' do + let(:validated_class) do + Class.new(ApplicationRecord) do + self.table_name = 'vulnerabilities' + + validates_with AnyFieldValidator, fields: %w(title description) + end + end + + it 'raises an error if no fields are defined' do + validated_object = validated_class.new + + expect(validated_object.valid?).to be_falsey + expect(validated_object.errors.messages) + .to eq(base: ["At least one field of %{one_of_required_fields} must be present" % + { one_of_required_fields: %w(title description) }]) + end + + it 'validates if only one field is present' do + validated_object = validated_class.new(title: 'Vulnerability title') + + expect(validated_object.valid?).to be_truthy + end + end + + context 'when validation is missing the fields parameter' do + let(:invalid_class) do + Class.new(ApplicationRecord) do + self.table_name = 'vulnerabilities' + + validates_with AnyFieldValidator + end + end + + it 'raises an error' do + expect { invalid_class.new }.to raise_error(RuntimeError) + end + end +end