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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/ci/reports.gitlab-ci.yml3
-rw-r--r--.rubocop_manual_todo.yml1
-rw-r--r--app/graphql/resolvers/ci/runners_resolver.rb43
-rw-r--r--app/graphql/types/ci/runner_sort_enum.rb13
-rw-r--r--app/graphql/types/query_type.rb6
-rw-r--r--app/models/ci/bridge.rb3
-rw-r--r--app/models/ci/build.rb10
-rw-r--r--app/models/ci/processable.rb4
-rw-r--r--app/policies/ci/build_policy.rb7
-rw-r--r--changelogs/unreleased/322604-add-a-check-that-the-metric-definitions-are-added-when-new-metrics.yml6
-rw-r--r--config/metrics/counts_28d/20210427102618_code_review_category_monthly_active_users.yml22
-rw-r--r--config/metrics/counts_28d/20210427103010_code_review_extension_category_monthly_active_users.yml22
-rw-r--r--config/metrics/counts_28d/20210427103119_code_review_group_monthly_active_users.yml22
-rw-r--r--config/metrics/counts_28d/20210427105033_pipeline_authoring_total_unique_counts_monthly.yml22
-rw-r--r--config/metrics/counts_28d/20210427213346_geo_secondary_web_oauth_users.yml22
-rw-r--r--config/metrics/counts_7d/20210427103328_code_review_group_monthly_active_users.yml22
-rw-r--r--config/metrics/counts_7d/20210427103407_code_review_category_monthly_active_users.yml22
-rw-r--r--config/metrics/counts_7d/20210427103452_code_review_extension_category_monthly_active_users.yml22
-rw-r--r--config/metrics/counts_7d/20210427105030_pipeline_authoring_total_unique_counts_weekly.yml22
-rw-r--r--config/metrics/counts_all/20210423005644_i_analytics_dev_ops_adoption.yml21
-rw-r--r--config/metrics/counts_all/20210427212450_geo_secondary_web_oauth_users.yml22
-rw-r--r--config/metrics/counts_all/20210428142406_users_viewing_analytics_group_devops_adoption.yml22
-rw-r--r--doc/administration/pages/index.md22
-rw-r--r--doc/api/graphql/reference/index.md51
-rw-r--r--doc/development/usage_ping/dictionary.md192
-rw-r--r--doc/update/index.md30
-rw-r--r--lib/gitlab/usage_data.rb2
-rw-r--r--lib/gitlab/usage_data_counters/known_events/analytics.yml5
-rwxr-xr-xscripts/review_apps/review-apps.sh2
-rw-r--r--spec/graphql/resolvers/ci/runners_resolver_spec.rb136
-rw-r--r--spec/graphql/types/query_type_spec.rb7
-rw-r--r--spec/lib/gitlab/import_export/attribute_configuration_spec.rb5
-rw-r--r--spec/lib/gitlab/import_export/references_configuration_spec.rb5
-rw-r--r--spec/lib/gitlab/usage/metric_definition_spec.rb7
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb1
-rw-r--r--spec/requests/api/graphql/ci/runners_spec.rb114
36 files changed, 897 insertions, 41 deletions
diff --git a/.gitlab/ci/reports.gitlab-ci.yml b/.gitlab/ci/reports.gitlab-ci.yml
index 65e4db5924b..0162996e3a9 100644
--- a/.gitlab/ci/reports.gitlab-ci.yml
+++ b/.gitlab/ci/reports.gitlab-ci.yml
@@ -38,6 +38,9 @@ eslint-sast:
nodejs-scan-sast:
rules: !reference [".reports:rules:sast", rules]
+semgrep-sast:
+ rules: !reference [".reports:rules:sast", rules]
+
.secret-analyzer:
extends: .default-retry
needs: []
diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml
index 9aded702c49..c3863b457ad 100644
--- a/.rubocop_manual_todo.yml
+++ b/.rubocop_manual_todo.yml
@@ -52,7 +52,6 @@ Rails/SaveBang:
- 'ee/spec/models/approval_project_rule_spec.rb'
- 'ee/spec/models/burndown_spec.rb'
- 'ee/spec/models/elasticsearch_indexed_namespace_spec.rb'
- - 'ee/spec/models/environment_spec.rb'
- 'ee/spec/models/epic_spec.rb'
- 'ee/spec/models/gitlab_subscription_spec.rb'
- 'ee/spec/models/issue_spec.rb'
diff --git a/app/graphql/resolvers/ci/runners_resolver.rb b/app/graphql/resolvers/ci/runners_resolver.rb
new file mode 100644
index 00000000000..710706325cc
--- /dev/null
+++ b/app/graphql/resolvers/ci/runners_resolver.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Ci
+ class RunnersResolver < BaseResolver
+ type Types::Ci::RunnerType.connection_type, null: true
+
+ argument :status, ::Types::Ci::RunnerStatusEnum,
+ required: false,
+ description: 'Filter runners by status.'
+
+ argument :type, ::Types::Ci::RunnerTypeEnum,
+ required: false,
+ description: 'Filter runners by type.'
+
+ argument :tag_list, [GraphQL::STRING_TYPE],
+ required: false,
+ description: 'Filter by tags associated with the runner (comma-separated or array).'
+
+ argument :sort, ::Types::Ci::RunnerSortEnum,
+ required: false,
+ description: 'Sort order of results.'
+
+ def resolve(**args)
+ ::Ci::RunnersFinder
+ .new(current_user: current_user, params: runners_finder_params(args))
+ .execute
+ end
+
+ private
+
+ def runners_finder_params(params)
+ {
+ status_status: params[:status]&.to_s,
+ type_type: params[:type],
+ tag_name: params[:tag_list],
+ search: params[:search],
+ sort: params[:sort]&.to_s
+ }.compact
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/runner_sort_enum.rb b/app/graphql/types/ci/runner_sort_enum.rb
new file mode 100644
index 00000000000..550e870316a
--- /dev/null
+++ b/app/graphql/types/ci/runner_sort_enum.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ class RunnerSortEnum < BaseEnum
+ graphql_name 'CiRunnerSort'
+ description 'Values for sorting runners'
+
+ value 'CONTACTED_ASC', 'Ordered by contacted_at in ascending order.', value: :contacted_asc
+ value 'CREATED_DESC', 'Ordered by created_date in descending order.', value: :created_date
+ end
+ end
+end
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index 57e5df51aa8..8b7b9f0107b 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -119,6 +119,12 @@ module Types
description: "Find a runner.",
feature_flag: :runner_graphql_query
+ field :runners, Types::Ci::RunnerType.connection_type,
+ null: true,
+ resolver: Resolvers::Ci::RunnersResolver,
+ description: "Find runners visible to the current user.",
+ feature_flag: :runner_graphql_query
+
field :ci_config, resolver: Resolvers::Ci::ConfigResolver, complexity: 126 # AUTHENTICATED_COMPLEXITY / 2 + 1
def design_management
diff --git a/app/models/ci/bridge.rb b/app/models/ci/bridge.rb
index 60f19e014c2..352229c64da 100644
--- a/app/models/ci/bridge.rb
+++ b/app/models/ci/bridge.rb
@@ -163,6 +163,9 @@ module Ci
def expanded_environment_name
end
+ def instantized_environment
+ end
+
def execute_hooks
raise NotImplementedError
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 2904bc1fd63..46fc87a6ea8 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -88,6 +88,16 @@ module Ci
end
end
+ # Initializing an object instead of fetching `persisted_environment` for avoiding unnecessary queries.
+ # We're planning to introduce a direct relationship between build and environment
+ # in https://gitlab.com/gitlab-org/gitlab/-/issues/326445 to let us to preload
+ # in batch.
+ def instantized_environment
+ return unless has_environment?
+
+ ::Environment.new(project: self.project, name: self.expanded_environment_name)
+ end
+
serialize :options # rubocop:disable Cop/ActiveRecordSerialize
serialize :yaml_variables, Gitlab::Serializer::Ci::Variables # rubocop:disable Cop/ActiveRecordSerialize
diff --git a/app/models/ci/processable.rb b/app/models/ci/processable.rb
index 3b61840805a..15c57550159 100644
--- a/app/models/ci/processable.rb
+++ b/app/models/ci/processable.rb
@@ -120,6 +120,10 @@ module Ci
raise NotImplementedError
end
+ def instantized_environment
+ raise NotImplementedError
+ end
+
override :all_met_to_become_pending?
def all_met_to_become_pending?
super && !with_resource_group?
diff --git a/app/policies/ci/build_policy.rb b/app/policies/ci/build_policy.rb
index 3e41b2a139f..6162a31c118 100644
--- a/app/policies/ci/build_policy.rb
+++ b/app/policies/ci/build_policy.rb
@@ -21,7 +21,7 @@ module Ci
end
# overridden in EE
- condition(:protected_environment_access) do
+ condition(:protected_environment) do
false
end
@@ -68,7 +68,10 @@ module Ci
rule { project_read_build }.enable :read_build_trace
rule { debug_mode & ~project_update_build }.prevent :read_build_trace
- rule { ~protected_environment_access & (protected_ref | archived) }.policy do
+ # Authorizing the user to access to protected entities.
+ # There is a "jailbreak" mode to exceptionally bypass the authorization,
+ # however, you should NEVER allow it, rather suspect it's a wrong feature/product design.
+ rule { ~can?(:jailbreak) & (archived | protected_ref | protected_environment) }.policy do
prevent :update_build
prevent :update_commit_status
prevent :erase_build
diff --git a/changelogs/unreleased/322604-add-a-check-that-the-metric-definitions-are-added-when-new-metrics.yml b/changelogs/unreleased/322604-add-a-check-that-the-metric-definitions-are-added-when-new-metrics.yml
new file mode 100644
index 00000000000..b3173ee3382
--- /dev/null
+++ b/changelogs/unreleased/322604-add-a-check-that-the-metric-definitions-are-added-when-new-metrics.yml
@@ -0,0 +1,6 @@
+---
+title: Add specs ensuring all metric definition key paths are present in Usage Ping
+ structure
+merge_request: 59314
+author:
+type: other
diff --git a/config/metrics/counts_28d/20210427102618_code_review_category_monthly_active_users.yml b/config/metrics/counts_28d/20210427102618_code_review_category_monthly_active_users.yml
new file mode 100644
index 00000000000..224fd6477cc
--- /dev/null
+++ b/config/metrics/counts_28d/20210427102618_code_review_category_monthly_active_users.yml
@@ -0,0 +1,22 @@
+---
+key_path: counts_monthly.aggregated_metrics.code_review_category_monthly_active_users
+name: "0"
+description:
+product_section:
+product_stage:
+product_group:
+product_category:
+value_type: number
+status: implemented
+milestone: "13.12"
+introduced_by_url:
+time_frame: 28d
+data_source:
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+skip_validation: true
diff --git a/config/metrics/counts_28d/20210427103010_code_review_extension_category_monthly_active_users.yml b/config/metrics/counts_28d/20210427103010_code_review_extension_category_monthly_active_users.yml
new file mode 100644
index 00000000000..ba872d725df
--- /dev/null
+++ b/config/metrics/counts_28d/20210427103010_code_review_extension_category_monthly_active_users.yml
@@ -0,0 +1,22 @@
+---
+key_path: counts_monthly.aggregated_metrics.code_review_extension_category_monthly_active_users
+name: "0"
+description:
+product_section:
+product_stage:
+product_group:
+product_category:
+value_type: number
+status: implemented
+milestone: "13.12"
+introduced_by_url:
+time_frame: 28d
+data_source:
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+skip_validation: true
diff --git a/config/metrics/counts_28d/20210427103119_code_review_group_monthly_active_users.yml b/config/metrics/counts_28d/20210427103119_code_review_group_monthly_active_users.yml
new file mode 100644
index 00000000000..e0e6bb0567d
--- /dev/null
+++ b/config/metrics/counts_28d/20210427103119_code_review_group_monthly_active_users.yml
@@ -0,0 +1,22 @@
+---
+key_path: counts_monthly.aggregated_metrics.code_review_group_monthly_active_users
+name: "0"
+description:
+product_section:
+product_stage:
+product_group:
+product_category:
+value_type: number
+status: implemented
+milestone: "13.12"
+introduced_by_url:
+time_frame: 28d
+data_source:
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+skip_validation: true
diff --git a/config/metrics/counts_28d/20210427105033_pipeline_authoring_total_unique_counts_monthly.yml b/config/metrics/counts_28d/20210427105033_pipeline_authoring_total_unique_counts_monthly.yml
new file mode 100644
index 00000000000..594df7ebe26
--- /dev/null
+++ b/config/metrics/counts_28d/20210427105033_pipeline_authoring_total_unique_counts_monthly.yml
@@ -0,0 +1,22 @@
+---
+key_path: redis_hll_counters.pipeline_authoring.pipeline_authoring_total_unique_counts_monthly
+name: "0"
+description:
+product_section:
+product_stage:
+product_group:
+product_category:
+value_type: number
+status: implemented
+milestone: "13.12"
+introduced_by_url:
+time_frame: 28d
+data_source:
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+skip_validation: true
diff --git a/config/metrics/counts_28d/20210427213346_geo_secondary_web_oauth_users.yml b/config/metrics/counts_28d/20210427213346_geo_secondary_web_oauth_users.yml
new file mode 100644
index 00000000000..0f68989cc1d
--- /dev/null
+++ b/config/metrics/counts_28d/20210427213346_geo_secondary_web_oauth_users.yml
@@ -0,0 +1,22 @@
+---
+key_path: usage_activity_by_stage_monthly.enablement.geo_secondary_web_oauth_users
+name: ""
+description:
+product_section:
+product_stage:
+product_group:
+product_category:
+value_type: number
+status: implemented
+milestone: "13.12"
+introduced_by_url:
+time_frame: 28d
+data_source:
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+skip_validation: true
diff --git a/config/metrics/counts_7d/20210427103328_code_review_group_monthly_active_users.yml b/config/metrics/counts_7d/20210427103328_code_review_group_monthly_active_users.yml
new file mode 100644
index 00000000000..31b36bc8ad7
--- /dev/null
+++ b/config/metrics/counts_7d/20210427103328_code_review_group_monthly_active_users.yml
@@ -0,0 +1,22 @@
+---
+key_path: counts_weekly.aggregated_metrics.code_review_group_monthly_active_users
+name: "0"
+description:
+product_section:
+product_stage:
+product_group:
+product_category:
+value_type: number
+status: implemented
+milestone: "13.12"
+introduced_by_url:
+time_frame: 7d
+data_source:
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+skip_validation: true
diff --git a/config/metrics/counts_7d/20210427103407_code_review_category_monthly_active_users.yml b/config/metrics/counts_7d/20210427103407_code_review_category_monthly_active_users.yml
new file mode 100644
index 00000000000..aaf9350cdaa
--- /dev/null
+++ b/config/metrics/counts_7d/20210427103407_code_review_category_monthly_active_users.yml
@@ -0,0 +1,22 @@
+---
+key_path: counts_weekly.aggregated_metrics.code_review_category_monthly_active_users
+name: "0"
+description:
+product_section:
+product_stage:
+product_group:
+product_category:
+value_type: number
+status: implemented
+milestone: "13.12"
+introduced_by_url:
+time_frame: 7d
+data_source:
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+skip_validation: true
diff --git a/config/metrics/counts_7d/20210427103452_code_review_extension_category_monthly_active_users.yml b/config/metrics/counts_7d/20210427103452_code_review_extension_category_monthly_active_users.yml
new file mode 100644
index 00000000000..ded67e19e10
--- /dev/null
+++ b/config/metrics/counts_7d/20210427103452_code_review_extension_category_monthly_active_users.yml
@@ -0,0 +1,22 @@
+---
+key_path: counts_weekly.aggregated_metrics.code_review_extension_category_monthly_active_users
+name: ""
+description:
+product_section:
+product_stage:
+product_group:
+product_category:
+value_type: number
+status: implemented
+milestone: "13.12"
+introduced_by_url:
+time_frame: 7d
+data_source:
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+skip_validation: true
diff --git a/config/metrics/counts_7d/20210427105030_pipeline_authoring_total_unique_counts_weekly.yml b/config/metrics/counts_7d/20210427105030_pipeline_authoring_total_unique_counts_weekly.yml
new file mode 100644
index 00000000000..7f782d90f8d
--- /dev/null
+++ b/config/metrics/counts_7d/20210427105030_pipeline_authoring_total_unique_counts_weekly.yml
@@ -0,0 +1,22 @@
+---
+key_path: redis_hll_counters.pipeline_authoring.pipeline_authoring_total_unique_counts_weekly
+name: "0"
+description:
+product_section:
+product_stage:
+product_group:
+product_category:
+value_type: number
+status: implemented
+milestone: "13.12"
+introduced_by_url:
+time_frame: 7d
+data_source:
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+skip_validation: true
diff --git a/config/metrics/counts_all/20210423005644_i_analytics_dev_ops_adoption.yml b/config/metrics/counts_all/20210423005644_i_analytics_dev_ops_adoption.yml
new file mode 100644
index 00000000000..54dffb5573a
--- /dev/null
+++ b/config/metrics/counts_all/20210423005644_i_analytics_dev_ops_adoption.yml
@@ -0,0 +1,21 @@
+---
+key_path: analytics_unique_visits.i_analytics_dev_ops_adoption
+name: "<please fill metric name, suggested format is: {subject}_{verb}{ing|ed}_{object} eg: users_creating_epics or merge_requests_viewed_in_single_file_mode>"
+description:
+product_section:
+product_stage:
+product_group:
+product_category:
+value_type: number
+status: implemented
+milestone: "13.11"
+introduced_by_url:
+time_frame: all
+data_source:
+distribution:
+- ce
+# tier:
+# - free
+# - premium
+# - ultimate
+skip_validation: true
diff --git a/config/metrics/counts_all/20210427212450_geo_secondary_web_oauth_users.yml b/config/metrics/counts_all/20210427212450_geo_secondary_web_oauth_users.yml
new file mode 100644
index 00000000000..0ce05fb84e2
--- /dev/null
+++ b/config/metrics/counts_all/20210427212450_geo_secondary_web_oauth_users.yml
@@ -0,0 +1,22 @@
+---
+key_path: usage_activity_by_stage.enablement.geo_secondary_web_oauth_users
+name: ""
+description:
+product_section:
+product_stage:
+product_group:
+product_category:
+value_type: number
+status: implemented
+milestone: "13.12"
+introduced_by_url:
+time_frame: all
+data_source:
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+skip_validation: true
diff --git a/config/metrics/counts_all/20210428142406_users_viewing_analytics_group_devops_adoption.yml b/config/metrics/counts_all/20210428142406_users_viewing_analytics_group_devops_adoption.yml
new file mode 100644
index 00000000000..c3fb808541a
--- /dev/null
+++ b/config/metrics/counts_all/20210428142406_users_viewing_analytics_group_devops_adoption.yml
@@ -0,0 +1,22 @@
+---
+key_path: analytics_unique_visits.users_viewing_analytics_group_devops_adoption
+name: "<please fill metric name, suggested format is: {subject}_{verb}{ing|ed}_{object} eg: users_creating_epics or merge_requests_viewed_in_single_file_mode>"
+description:
+product_section:
+product_stage:
+product_group:
+product_category:
+value_type: number
+status: implemented
+milestone: "13.12"
+introduced_by_url:
+time_frame: all
+data_source:
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+skip_validation: true
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index bf2aba9f88c..c93ae90deb6 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -253,6 +253,7 @@ control over how the Pages daemon runs and serves content in your environment.
| `gitlab_server` | Server to use for authentication when access control is enabled; defaults to GitLab `external_url`. |
| `headers` | Specify any additional http headers that should be sent to the client with each response. Multiple headers can be given as an array, header and value as one string, for example `['my-header: myvalue', 'my-other-header: my-other-value']` |
| `inplace_chroot` | On [systems that don't support bind-mounts](index.md#additional-configuration-for-docker-container), this instructs GitLab Pages to `chroot` into its `pages_path` directory. Some caveats exist when using in-place `chroot`; refer to the GitLab Pages [README](https://gitlab.com/gitlab-org/gitlab-pages/blob/master/README.md#caveats) for more information. |
+| `enable_disk` | Allows the GitLab Pages daemon to serve content from disk. Shall be disabled if shared disk storage isn't available. |
| `insecure_ciphers` | Use default list of cipher suites, may contain insecure ones like 3DES and RC4. |
| `internal_gitlab_server` | Internal GitLab server address used exclusively for API requests. Useful if you want to send that traffic over an internal load balancer. Defaults to GitLab `external_url`. |
| `listen_proxy` | The addresses to listen on for reverse-proxy requests. Pages binds to these addresses' network sockets and receives incoming requests from them. Sets the value of `proxy_pass` in `$nginx-dir/conf/gitlab-pages.conf`. |
@@ -1299,3 +1300,24 @@ Once added, reconfigure with `sudo gitlab-ctl reconfigure` and restart GitLab wi
Verify that the **Callback URL**/Redirect URI in the GitLab Pages [System OAuth application](../../integration/oauth_provider.md#instance-wide-applications)
is using the protocol (HTTP or HTTPS) that `pages_external_url` is configured to use.
+
+### 500 error `cannot serve from disk`
+
+If you get a 500 response from Pages and encounter an error similar to:
+
+```plaintext
+ERRO[0145] cannot serve from disk error="gitlab: disk access is disabled via enable-disk=false" project_id=27 source_path="file:///shared/pages/@hashed/67/06/670671cd97404156226e507973f2ab8330d3022ca96e0c93bdbdb320c41adcaf/pages_deployments/14/artifacts.zip" source_type=zip
+```
+
+It means that GitLab Rails is telling GitLab Pages to serve content from a location on disk,
+however, GitLab Pages was configured to disable disk access.
+
+To enable disk access:
+
+1. Enable disk access for GitLab Pages in `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
+ gitlab_pages['enable_disk'] = true
+ ```
+
+1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index c14edc2180f..c9a015f0950 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -322,6 +322,25 @@ Returns [`RunnerSetup`](#runnersetup).
| <a id="queryrunnersetupplatform"></a>`platform` | [`String!`](#string) | Platform to generate the instructions for. |
| <a id="queryrunnersetupprojectid"></a>`projectId` **{warning-solid}** | [`ProjectID`](#projectid) | **Deprecated** in 13.11. No longer used. |
+### `Query.runners`
+
+Find runners visible to the current user. Available only when feature flag `runner_graphql_query` is enabled.
+
+Returns [`CiRunnerConnection`](#cirunnerconnection).
+
+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="queryrunnerssort"></a>`sort` | [`CiRunnerSort`](#cirunnersort) | Sort order of results. |
+| <a id="queryrunnersstatus"></a>`status` | [`CiRunnerStatus`](#cirunnerstatus) | Filter runners by status. |
+| <a id="queryrunnerstaglist"></a>`tagList` | [`[String!]`](#string) | Filter by tags associated with the runner (comma-separated or array). |
+| <a id="queryrunnerstype"></a>`type` | [`CiRunnerType`](#cirunnertype) | Filter runners by type. |
+
### `Query.snippets`
Find Snippets visible to the current user.
@@ -4600,6 +4619,29 @@ The edge type for [`CiJob`](#cijob).
| <a id="cijobedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="cijobedgenode"></a>`node` | [`CiJob`](#cijob) | The item at the end of the edge. |
+#### `CiRunnerConnection`
+
+The connection type for [`CiRunner`](#cirunner).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="cirunnerconnectionedges"></a>`edges` | [`[CiRunnerEdge]`](#cirunneredge) | A list of edges. |
+| <a id="cirunnerconnectionnodes"></a>`nodes` | [`[CiRunner]`](#cirunner) | A list of nodes. |
+| <a id="cirunnerconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
+
+#### `CiRunnerEdge`
+
+The edge type for [`CiRunner`](#cirunner).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="cirunneredgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
+| <a id="cirunneredgenode"></a>`node` | [`CiRunner`](#cirunner) | The item at the end of the edge. |
+
#### `CiStageConnection`
The connection type for [`CiStage`](#cistage).
@@ -13647,6 +13689,15 @@ Values for YAML processor result.
| <a id="cirunneraccesslevelnot_protected"></a>`NOT_PROTECTED` | A runner that is not protected. |
| <a id="cirunneraccesslevelref_protected"></a>`REF_PROTECTED` | A runner that is ref protected. |
+### `CiRunnerSort`
+
+Values for sorting runners.
+
+| Value | Description |
+| ----- | ----------- |
+| <a id="cirunnersortcontacted_asc"></a>`CONTACTED_ASC` | Ordered by contacted_at in ascending order. |
+| <a id="cirunnersortcreated_desc"></a>`CREATED_DESC` | Ordered by created_date in descending order. |
+
### `CiRunnerStatus`
| Value | Description |
diff --git a/doc/development/usage_ping/dictionary.md b/doc/development/usage_ping/dictionary.md
index 1ce2695901a..75d65f8e5df 100644
--- a/doc/development/usage_ping/dictionary.md
+++ b/doc/development/usage_ping/dictionary.md
@@ -154,6 +154,18 @@ Status: `data_available`
Tiers: `free`
+### `analytics_unique_visits.i_analytics_dev_ops_adoption`
+
+Missing description
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210423005644_i_analytics_dev_ops_adoption.yml)
+
+Group: ``
+
+Status: `implemented`
+
+Tiers:
+
### `analytics_unique_visits.i_analytics_dev_ops_score`
Unique visitors to /-/instance_statistics/dev_ops_score
@@ -262,6 +274,18 @@ Status: `data_available`
Tiers: `free`
+### `analytics_unique_visits.users_viewing_analytics_group_devops_adoption`
+
+Missing description
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210428142406_users_viewing_analytics_group_devops_adoption.yml)
+
+Group: ``
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
### `compliance_unique_visits.a_compliance_audit_events_api`
Missing description
@@ -6562,6 +6586,42 @@ Status: `data_available`
Tiers: `free`
+### `counts_monthly.aggregated_metrics.code_review_category_monthly_active_users`
+
+Missing description
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210427102618_code_review_category_monthly_active_users.yml)
+
+Group: ``
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `counts_monthly.aggregated_metrics.code_review_extension_category_monthly_active_users`
+
+Missing description
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210427103010_code_review_extension_category_monthly_active_users.yml)
+
+Group: ``
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `counts_monthly.aggregated_metrics.code_review_group_monthly_active_users`
+
+Missing description
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210427103119_code_review_group_monthly_active_users.yml)
+
+Group: ``
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
### `counts_monthly.aggregated_metrics.compliance_features_track_unique_visits_union`
Missing description
@@ -6730,6 +6790,42 @@ Status: `data_available`
Tiers: `free`
+### `counts_weekly.aggregated_metrics.code_review_category_monthly_active_users`
+
+Missing description
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210427103407_code_review_category_monthly_active_users.yml)
+
+Group: ``
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `counts_weekly.aggregated_metrics.code_review_extension_category_monthly_active_users`
+
+Missing description
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210427103452_code_review_extension_category_monthly_active_users.yml)
+
+Group: ``
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `counts_weekly.aggregated_metrics.code_review_group_monthly_active_users`
+
+Missing description
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210427103328_code_review_group_monthly_active_users.yml)
+
+Group: ``
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
### `counts_weekly.aggregated_metrics.compliance_features_track_unique_visits_union`
Missing description
@@ -11422,30 +11518,6 @@ Status: `data_available`
Tiers:
-### `redis_hll_counters.incident_management.i_incident_management_oncall_notification_sent_monthly`
-
-Count of unique users to receive a notification while on-call
-
-[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_28d/20210405222005_i_incident_management_oncall_notification_sent_monthly.yml)
-
-Group: `group::monitor`
-
-Status: `implemented`
-
-Tiers: `premium`, `ultimate`
-
-### `redis_hll_counters.incident_management.i_incident_management_oncall_notification_sent_weekly`
-
-Count of unique users to receive a notification while on-call
-
-[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_7d/20210405220139_i_incident_management_oncall_notification_sent_weekly.yml)
-
-Group: `group::monitor`
-
-Status: `implemented`
-
-Tiers: `premium`, `ultimate`
-
### `redis_hll_counters.incident_management.incident_management_alert_assigned_monthly`
Missing description
@@ -11830,6 +11902,30 @@ Status: `data_available`
Tiers:
+### `redis_hll_counters.incident_management_oncall.i_incident_management_oncall_notification_sent_monthly`
+
+Count of unique users to receive a notification while on-call
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_28d/20210405222005_i_incident_management_oncall_notification_sent_monthly.yml)
+
+Group: `group::monitor`
+
+Status: `implemented`
+
+Tiers: `premium`, `ultimate`
+
+### `redis_hll_counters.incident_management_oncall.i_incident_management_oncall_notification_sent_weekly`
+
+Count of unique users to receive a notification while on-call
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_7d/20210405220139_i_incident_management_oncall_notification_sent_weekly.yml)
+
+Group: `group::monitor`
+
+Status: `implemented`
+
+Tiers: `premium`, `ultimate`
+
### `redis_hll_counters.issues_edit.g_project_management_issue_added_to_epic_monthly`
Count of MAU adding an issue to an epic
@@ -12694,6 +12790,30 @@ Status: `data_available`
Tiers: `free`, `premium`, `ultimate`
+### `redis_hll_counters.pipeline_authoring.pipeline_authoring_total_unique_counts_monthly`
+
+Missing description
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210427105033_pipeline_authoring_total_unique_counts_monthly.yml)
+
+Group: ``
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.pipeline_authoring.pipeline_authoring_total_unique_counts_weekly`
+
+Missing description
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210427105030_pipeline_authoring_total_unique_counts_weekly.yml)
+
+Group: ``
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
### `redis_hll_counters.quickactions.i_quickactions_approve_monthly`
Count of MAU using the `/approve` quick action
@@ -15948,6 +16068,18 @@ Status: `data_available`
Tiers: `premium`, `ultimate`
+### `usage_activity_by_stage.enablement.geo_secondary_web_oauth_users`
+
+Missing description
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210427212450_geo_secondary_web_oauth_users.yml)
+
+Group: ``
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
### `usage_activity_by_stage.manage.bulk_imports.gitlab`
Distinct count of users that triggered an import using the Group Migration tool
@@ -17894,6 +18026,18 @@ Status: `data_available`
Tiers: `premium`, `ultimate`
+### `usage_activity_by_stage_monthly.enablement.geo_secondary_web_oauth_users`
+
+Missing description
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210427213346_geo_secondary_web_oauth_users.yml)
+
+Group: ``
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
### `usage_activity_by_stage_monthly.manage.bulk_imports.gitlab`
Missing description
diff --git a/doc/update/index.md b/doc/update/index.md
index 3c2ea8e0d92..5c6f47d2b9c 100644
--- a/doc/update/index.md
+++ b/doc/update/index.md
@@ -374,6 +374,36 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap
Git 2.31.x and later is required. We recommend you use the
[Git version provided by Gitaly](../install/installation.md#git).
+### 13.9.0
+
+We've detected an issue [with a column rename](https://gitlab.com/gitlab-org/gitlab/-/issues/324160)
+that may prevent upgrades to GitLab 13.9.0, 13.9.1, 13.9.2 and 13.9.3.
+We are working on a patch, but until a fixed version is released, you can manually complete
+the zero-downtime upgrade:
+
+1. Before running the final `sudo gitlab-rake db:migrate` command on the deploy node,
+ execute the following queries using the PostgreSQL console (or `sudo gitlab-psql`)
+ to drop the problematic triggers:
+
+ ```sql
+ drop trigger trigger_e40a6f1858e6 on application_settings;
+ drop trigger trigger_0d588df444c8 on application_settings;
+ drop trigger trigger_1572cbc9a15f on application_settings;
+ drop trigger trigger_22a39c5c25f3 on application_settings;
+ ```
+
+1. Run the final migrations:
+
+ ```shell
+ sudo gitlab-rake db:migrate
+ ```
+
+If you have already run the final `sudo gitlab-rake db:migrate` command on the deploy node and have
+encountered the [column rename issue](https://gitlab.com/gitlab-org/gitlab/-/issues/324160), you can still
+follow the previous steps to complete the update.
+
+More details are available [in this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/324160).
+
### 13.6.0
Ruby 2.7.2 is required. GitLab does not start with Ruby 2.6.6 or older versions.
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index 40e386e717b..b1ba529d4a4 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -427,7 +427,7 @@ module Gitlab
def services_usage
# rubocop: disable UsageData/LargeTable:
- Integration.available_services_names.each_with_object({}) do |service_name, response|
+ Integration.available_services_names(include_dev: false).each_with_object({}) do |service_name, response|
service_type = Integration.service_name_to_type(service_name)
response["projects_#{service_name}_active".to_sym] = count(Integration.active.where.not(project: nil).where(type: service_type))
diff --git a/lib/gitlab/usage_data_counters/known_events/analytics.yml b/lib/gitlab/usage_data_counters/known_events/analytics.yml
index db0bed79ac4..e4f20b61901 100644
--- a/lib/gitlab/usage_data_counters/known_events/analytics.yml
+++ b/lib/gitlab/usage_data_counters/known_events/analytics.yml
@@ -8,11 +8,6 @@
redis_slot: analytics
aggregation: weekly
feature_flag: track_unique_visits
-- name: g_analytics_merge_request
- category: analytics
- redis_slot: analytics
- aggregation: weekly
- feature_flag: track_unique_visits
- name: i_analytics_dev_ops_score
category: analytics
redis_slot: analytics
diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh
index e2c905e9b6b..5b52797d285 100755
--- a/scripts/review_apps/review-apps.sh
+++ b/scripts/review_apps/review-apps.sh
@@ -331,7 +331,7 @@ HELM_CMD=$(cat << EOF
--namespace="${namespace}" \
--install \
--wait \
- --timeout 15m \
+ --timeout "${HELM_INSTALL_TIMEOUT:-20m}" \
--set ci.branch="${CI_COMMIT_REF_NAME}" \
--set ci.commit.sha="${CI_COMMIT_SHORT_SHA}" \
--set ci.job.url="${CI_JOB_URL}" \
diff --git a/spec/graphql/resolvers/ci/runners_resolver_spec.rb b/spec/graphql/resolvers/ci/runners_resolver_spec.rb
new file mode 100644
index 00000000000..006d6785506
--- /dev/null
+++ b/spec/graphql/resolvers/ci/runners_resolver_spec.rb
@@ -0,0 +1,136 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::Ci::RunnersResolver do
+ include GraphqlHelpers
+
+ let_it_be(:user) { create_default(:user, :admin) }
+ let_it_be(:group) { create(:group, :public) }
+ let_it_be(:project) { create(:project, :repository, :public) }
+
+ let_it_be(:inactive_project_runner) do
+ create(:ci_runner, :project, projects: [project], active: false, contacted_at: 1.minute.ago, tag_list: %w(project_runner))
+ end
+
+ let_it_be(:offline_project_runner) do
+ create(:ci_runner, :project, projects: [project], contacted_at: 1.day.ago, tag_list: %w(project_runner active_runner))
+ end
+
+ let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group], contacted_at: 1.second.ago) }
+ let_it_be(:instance_runner) { create(:ci_runner, :instance, contacted_at: 2.minutes.ago, tag_list: %w(instance_runner active_runner)) }
+
+ describe '#resolve' do
+ subject { resolve(described_class, ctx: { current_user: user }, args: args).items.to_a }
+
+ let(:args) do
+ {}
+ end
+
+ context 'without sort' do
+ it 'returns all the runners' do
+ is_expected.to contain_exactly(inactive_project_runner, offline_project_runner, group_runner, instance_runner)
+ end
+ end
+
+ context 'with a sort argument' do
+ context "set to :contacted_asc" do
+ let(:args) do
+ { sort: :contacted_asc }
+ end
+
+ it { is_expected.to eq([offline_project_runner, instance_runner, inactive_project_runner, group_runner]) }
+ end
+
+ context "set to :created_date" do
+ let(:args) do
+ { sort: :created_date }
+ end
+
+ it { is_expected.to eq([instance_runner, group_runner, offline_project_runner, inactive_project_runner]) }
+ end
+ end
+
+ context 'when type is filtered' do
+ let(:args) do
+ { type: runner_type.to_s }
+ end
+
+ context 'to instance runners' do
+ let(:runner_type) { :instance_type }
+
+ it 'returns the instance runner' do
+ is_expected.to contain_exactly(instance_runner)
+ end
+ end
+
+ context 'to group runners' do
+ let(:runner_type) { :group_type }
+
+ it 'returns the group runner' do
+ is_expected.to contain_exactly(group_runner)
+ end
+ end
+
+ context 'to project runners' do
+ let(:runner_type) { :project_type }
+
+ it 'returns the project runner' do
+ is_expected.to contain_exactly(inactive_project_runner, offline_project_runner)
+ end
+ end
+ end
+
+ context 'when status is filtered' do
+ let(:args) do
+ { status: runner_status.to_s }
+ end
+
+ context 'to active runners' do
+ let(:runner_status) { :active }
+
+ it 'returns the instance and group runners' do
+ is_expected.to contain_exactly(offline_project_runner, group_runner, instance_runner)
+ end
+ end
+
+ context 'to offline runners' do
+ let(:runner_status) { :offline }
+
+ it 'returns the offline project runner' do
+ is_expected.to contain_exactly(offline_project_runner)
+ end
+ end
+ end
+
+ context 'when tag list is filtered' do
+ let(:args) do
+ { tag_list: tag_list }
+ end
+
+ context 'with "project_runner" tag' do
+ let(:tag_list) { ['project_runner'] }
+
+ it 'returns the project_runner runners' do
+ is_expected.to contain_exactly(offline_project_runner, inactive_project_runner)
+ end
+ end
+
+ context 'with "project_runner" and "active_runner" tags as comma-separated string' do
+ let(:tag_list) { ['project_runner,active_runner'] }
+
+ it 'returns the offline_project_runner runner' do
+ is_expected.to contain_exactly(offline_project_runner)
+ end
+ end
+
+ context 'with "active_runner" and "instance_runner" tags as array' do
+ let(:tag_list) { %w[instance_runner active_runner] }
+
+ it 'returns the offline_project_runner runner' do
+ is_expected.to contain_exactly(instance_runner)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/graphql/types/query_type_spec.rb b/spec/graphql/types/query_type_spec.rb
index f0e80fa8f14..9a8f2090cc1 100644
--- a/spec/graphql/types/query_type_spec.rb
+++ b/spec/graphql/types/query_type_spec.rb
@@ -25,6 +25,7 @@ RSpec.describe GitlabSchema.types['Query'] do
usage_trends_measurements
runner_platforms
runner
+ runners
]
expect(described_class).to have_graphql_fields(*expected_fields).at_least
@@ -91,6 +92,12 @@ RSpec.describe GitlabSchema.types['Query'] do
it { is_expected.to have_graphql_type(Types::Ci::RunnerType) }
end
+ describe 'runners field' do
+ subject { described_class.fields['runners'] }
+
+ it { is_expected.to have_graphql_type(Types::Ci::RunnerType.connection_type) }
+ end
+
describe 'runner_platforms field' do
subject { described_class.fields['runnerPlatforms'] }
diff --git a/spec/lib/gitlab/import_export/attribute_configuration_spec.rb b/spec/lib/gitlab/import_export/attribute_configuration_spec.rb
index 0581f07dd3f..7e17d56def0 100644
--- a/spec/lib/gitlab/import_export/attribute_configuration_spec.rb
+++ b/spec/lib/gitlab/import_export/attribute_configuration_spec.rb
@@ -35,8 +35,9 @@ RSpec.describe 'Import/Export attribute configuration' do
<<-MSG
It looks like #{relation_class}, which is exported using the project Import/Export, has new attributes: #{new_attributes.join(',')}
- Please add the attribute(s) to SAFE_MODEL_ATTRIBUTES if you consider this can be exported.
- Please blacklist the attribute(s) in IMPORT_EXPORT_CONFIG by adding it to its correspondent
+ Please add the attribute(s) to SAFE_MODEL_ATTRIBUTES if they can be exported.
+
+ Please denylist the attribute(s) in IMPORT_EXPORT_CONFIG by adding it to its corresponding
model in the +excluded_attributes+ section.
SAFE_MODEL_ATTRIBUTES: #{File.expand_path(safe_attributes_file)}
diff --git a/spec/lib/gitlab/import_export/references_configuration_spec.rb b/spec/lib/gitlab/import_export/references_configuration_spec.rb
index 2934d0059ee..6320fbed975 100644
--- a/spec/lib/gitlab/import_export/references_configuration_spec.rb
+++ b/spec/lib/gitlab/import_export/references_configuration_spec.rb
@@ -38,8 +38,9 @@ RSpec.describe 'Import/Export Project configuration' do
<<-MSG
It looks like #{relation_class}, which is exported using the project Import/Export, has references: #{prohibited_keys.join(',')}
- Please replace it with actual relation in IMPORT_EXPORT_CONFIG if you consider this can be exported.
- Please blacklist the attribute(s) in IMPORT_EXPORT_CONFIG by adding it to its correspondent
+ Please replace it with actual relation in IMPORT_EXPORT_CONFIG if they can be exported.
+
+ Please denylist the attribute(s) in IMPORT_EXPORT_CONFIG by adding it to its corresponding
model in the +excluded_attributes+ section.
IMPORT_EXPORT_CONFIG: #{Gitlab::ImportExport.config_file}
diff --git a/spec/lib/gitlab/usage/metric_definition_spec.rb b/spec/lib/gitlab/usage/metric_definition_spec.rb
index 65cd4300ee6..92e51b8ea23 100644
--- a/spec/lib/gitlab/usage/metric_definition_spec.rb
+++ b/spec/lib/gitlab/usage/metric_definition_spec.rb
@@ -38,6 +38,11 @@ RSpec.describe Gitlab::Usage::MetricDefinition do
File.write(path, content)
end
+ after do
+ # Reset memoized `definitions` result
+ described_class.instance_variable_set(:@definitions, nil)
+ end
+
it 'has all definitons valid' do
expect { described_class.definitions }.not_to raise_error(Gitlab::Usage::Metric::InvalidMetricError)
end
@@ -193,8 +198,6 @@ RSpec.describe Gitlab::Usage::MetricDefinition do
File.join(metric2, '**', '*.yml')
]
)
- # Reset memoized `definitions` result
- described_class.instance_variable_set(:@definitions, nil)
end
after do
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index b11cc6d8f6e..d4b6ac09261 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -1332,7 +1332,6 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
'i_analytics_dev_ops_score' => 123,
'i_analytics_instance_statistics' => 123,
'p_analytics_merge_request' => 123,
- 'g_analytics_merge_request' => 123,
'i_analytics_dev_ops_adoption' => 123,
'users_viewing_analytics_group_devops_adoption' => 123,
'analytics_unique_visits_for_any_target' => 543,
diff --git a/spec/requests/api/graphql/ci/runners_spec.rb b/spec/requests/api/graphql/ci/runners_spec.rb
new file mode 100644
index 00000000000..778fe5b129e
--- /dev/null
+++ b/spec/requests/api/graphql/ci/runners_spec.rb
@@ -0,0 +1,114 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe 'Query.runners' do
+ include GraphqlHelpers
+
+ let_it_be(:current_user) { create_default(:user, :admin) }
+
+ describe 'Query.runners' do
+ let_it_be(:project) { create(:project, :repository, :public) }
+ let_it_be(:instance_runner) { create(:ci_runner, :instance, version: 'abc', revision: '123', description: 'Instance runner', ip_address: '127.0.0.1') }
+ let_it_be(:project_runner) { create(:ci_runner, :project, active: false, version: 'def', revision: '456', description: 'Project runner', projects: [project], ip_address: '127.0.0.1') }
+
+ let(:runners_graphql_data) { graphql_data['runners'] }
+
+ let(:params) { {} }
+
+ let(:fields) do
+ <<~QUERY
+ nodes {
+ #{all_graphql_fields_for('CiRunner')}
+ }
+ QUERY
+ end
+
+ let(:query) do
+ %(
+ query {
+ runners(type:#{runner_type},status:#{status}) {
+ #{fields}
+ }
+ }
+ )
+ end
+
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+
+ shared_examples 'a working graphql query returning expected runner' do
+ it_behaves_like 'a working graphql query'
+
+ it 'returns expected runner' do
+ expect(runners_graphql_data['nodes'].map { |n| n['id'] }).to contain_exactly(expected_runner.to_global_id.to_s)
+ end
+ end
+
+ context 'runner_type is INSTANCE_TYPE and status is ACTIVE' do
+ let(:runner_type) { 'INSTANCE_TYPE' }
+ let(:status) { 'ACTIVE' }
+
+ let!(:expected_runner) { instance_runner }
+
+ it_behaves_like 'a working graphql query returning expected runner'
+ end
+
+ context 'runner_type is PROJECT_TYPE and status is NOT_CONNECTED' do
+ let(:runner_type) { 'PROJECT_TYPE' }
+ let(:status) { 'NOT_CONNECTED' }
+
+ let!(:expected_runner) { project_runner }
+
+ it_behaves_like 'a working graphql query returning expected runner'
+ end
+ end
+
+ describe 'pagination' do
+ let(:data_path) { [:runners] }
+
+ def pagination_query(params)
+ graphql_query_for(:runners, params, "#{page_info} nodes { id }")
+ end
+
+ def pagination_results_data(runners)
+ runners.map { |runner| GitlabSchema.parse_gid(runner['id'], expected_type: ::Ci::Runner).model_id.to_i }
+ end
+
+ let_it_be(:runners) do
+ common_args = {
+ version: 'abc',
+ revision: '123',
+ ip_address: '127.0.0.1'
+ }
+
+ [
+ create(:ci_runner, :instance, created_at: 4.days.ago, contacted_at: 3.days.ago, **common_args),
+ create(:ci_runner, :instance, created_at: 30.hours.ago, contacted_at: 1.day.ago, **common_args),
+ create(:ci_runner, :instance, created_at: 1.day.ago, contacted_at: 1.hour.ago, **common_args),
+ create(:ci_runner, :instance, created_at: 2.days.ago, contacted_at: 2.days.ago, **common_args),
+ create(:ci_runner, :instance, created_at: 3.days.ago, contacted_at: 1.second.ago, **common_args)
+ ]
+ end
+
+ context 'when sorted by contacted_at ascending' do
+ let(:ordered_runners) { runners.sort_by(&:contacted_at) }
+
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { :CONTACTED_ASC }
+ let(:first_param) { 2 }
+ let(:expected_results) { ordered_runners.map(&:id) }
+ end
+ end
+
+ context 'when sorted by created_at' do
+ let(:ordered_runners) { runners.sort_by(&:created_at).reverse }
+
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { :CREATED_DESC }
+ let(:first_param) { 2 }
+ let(:expected_results) { ordered_runners.map(&:id) }
+ end
+ end
+ end
+end