From 8caeaa1e9a922acb719453866d93e1180a6e2dbb Mon Sep 17 00:00:00 2001 From: Vladimir Shushlin <vshushlin@gitlab.com> Date: Fri, 2 Jun 2023 13:32:24 +0000 Subject: [PATCH] Add upgrade status to RunnerManager in GraphQL Changelog: added EE: true --- app/graphql/types/ci/runner_manager_type.rb | 2 + doc/api/graphql/reference/index.md | 1 + .../ee/types/ci/runner_manager_type.rb | 38 +++++++++++++++++++ ee/app/graphql/ee/types/ci/runner_type.rb | 4 +- ee/app/policies/ee/global_policy.rb | 12 ++++++ .../types/ci/runner_manager_type_spec.rb | 13 +++++++ ee/spec/policies/global_policy_spec.rb | 22 +++++++++++ .../requests/api/graphql/ci/runner_spec.rb | 13 ++++++- .../types/ci/runner_manager_type_spec.rb | 2 +- spec/requests/api/graphql/ci/jobs_spec.rb | 4 +- 10 files changed, 104 insertions(+), 7 deletions(-) create mode 100644 ee/app/graphql/ee/types/ci/runner_manager_type.rb create mode 100644 ee/spec/graphql/types/ci/runner_manager_type_spec.rb diff --git a/app/graphql/types/ci/runner_manager_type.rb b/app/graphql/types/ci/runner_manager_type.rb index 2a5053f8f0745..9c89b6537ea27 100644 --- a/app/graphql/types/ci/runner_manager_type.rb +++ b/app/graphql/types/ci/runner_manager_type.rb @@ -47,3 +47,5 @@ def executor_name end end end + +Types::Ci::RunnerManagerType.prepend_mod_with('Types::Ci::RunnerManagerType') diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 810dae24f2509..9f6baa5495b42 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -12941,6 +12941,7 @@ Returns [`CiRunnerStatus!`](#cirunnerstatus). | <a id="cirunnermanagerrunner"></a>`runner` | [`CiRunner`](#cirunner) | Runner configuration for the runner manager. | | <a id="cirunnermanagerstatus"></a>`status` | [`CiRunnerStatus!`](#cirunnerstatus) | Status of the runner manager. | | <a id="cirunnermanagersystemid"></a>`systemId` | [`String!`](#string) | System ID associated with the runner manager. | +| <a id="cirunnermanagerupgradestatus"></a>`upgradeStatus` **{warning-solid}** | [`CiRunnerUpgradeStatus`](#cirunnerupgradestatus) | **Introduced** in 16.1. This feature is an Experiment. It can be changed or removed at any time. Availability of upgrades for the runner manager. | | <a id="cirunnermanagerversion"></a>`version` | [`String`](#string) | Version of the runner. | ### `CiSecureFileRegistry` diff --git a/ee/app/graphql/ee/types/ci/runner_manager_type.rb b/ee/app/graphql/ee/types/ci/runner_manager_type.rb new file mode 100644 index 0000000000000..56e2b8248c17b --- /dev/null +++ b/ee/app/graphql/ee/types/ci/runner_manager_type.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module EE + module Types + module Ci + module RunnerManagerType + extend ActiveSupport::Concern + + RUNNER_UPGRADE_STATUS_TRANSLATIONS = { + error: nil + }.freeze + + prepended do + field :upgrade_status, ::Types::Ci::RunnerUpgradeStatusEnum, + null: true, + description: 'Availability of upgrades for the runner manager.', + alpha: { milestone: '16.1' } + + def upgrade_status + return unless upgrade_status_available? + + _, status = ::Gitlab::Ci::RunnerUpgradeCheck.new(::Gitlab::VERSION) + .check_runner_upgrade_suggestion(runner_manager.version) + RUNNER_UPGRADE_STATUS_TRANSLATIONS.fetch(status, status) + end + + private + + def upgrade_status_available? + return false unless ::Gitlab::Ci::RunnerReleases.instance.enabled? + + Ability.allowed?(current_user, :read_runner_upgrade_status) + end + end + end + end + end +end diff --git a/ee/app/graphql/ee/types/ci/runner_type.rb b/ee/app/graphql/ee/types/ci/runner_type.rb index 0e8ccb83c5de2..00f7627090b6f 100644 --- a/ee/app/graphql/ee/types/ci/runner_type.rb +++ b/ee/app/graphql/ee/types/ci/runner_type.rb @@ -19,6 +19,8 @@ module RunnerType null: true, description: 'Private projects\' "minutes cost factor" associated with the runner (GitLab.com only).' + # TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/411945 + # this should take the maximum status from runner managers field :upgrade_status, ::Types::Ci::RunnerUpgradeStatusEnum, null: true, description: 'Availability of upgrades for the runner.', @@ -37,7 +39,7 @@ def upgrade_status def upgrade_status_available? return false unless ::Gitlab::Ci::RunnerReleases.instance.enabled? - License.feature_available?(:runner_upgrade_management) || current_user&.has_paid_namespace? + Ability.allowed?(current_user, :read_runner_upgrade_status) end end end diff --git a/ee/app/policies/ee/global_policy.rb b/ee/app/policies/ee/global_policy.rb index dedf427637cd5..0535ecbd916ba 100644 --- a/ee/app/policies/ee/global_policy.rb +++ b/ee/app/policies/ee/global_policy.rb @@ -41,6 +41,10 @@ module GlobalPolicy ::License.feature_available?(:runner_jobs_statistics) end + condition(:runner_upgrade_management_available) do + License.feature_available?(:runner_upgrade_management) + end + condition(:service_accounts_available) do ::License.feature_available?(:service_accounts) end @@ -61,6 +65,12 @@ module GlobalPolicy accessible_root_groups.reject(&:code_suggestions_enabled?).any? end + condition(:user_has_paid_namespace) do + next false unless @user + + @user.has_paid_namespace? + end + rule { ~anonymous & operations_dashboard_available }.enable :read_operations_dashboard rule { ~anonymous & remote_development_available }.enable :read_workspace @@ -106,6 +116,8 @@ module GlobalPolicy rule { code_suggestions_enabled }.enable :access_code_suggestions rule { code_suggestions_disabled_by_group }.prevent :access_code_suggestions + + rule { runner_upgrade_management_available | user_has_paid_namespace }.enable :read_runner_upgrade_status end end end diff --git a/ee/spec/graphql/types/ci/runner_manager_type_spec.rb b/ee/spec/graphql/types/ci/runner_manager_type_spec.rb new file mode 100644 index 0000000000000..58d176e86dd30 --- /dev/null +++ b/ee/spec/graphql/types/ci/runner_manager_type_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['CiRunnerManager'], feature_category: :runner_fleet do + it { expect(described_class.graphql_name).to eq('CiRunnerManager') } + + it 'includes the ee specific fields' do + expected_fields = %w[upgrade_status] + + expect(described_class).to include_graphql_fields(*expected_fields) + end +end diff --git a/ee/spec/policies/global_policy_spec.rb b/ee/spec/policies/global_policy_spec.rb index e93e125783147..05af1ae977dbc 100644 --- a/ee/spec/policies/global_policy_spec.rb +++ b/ee/spec/policies/global_policy_spec.rb @@ -396,6 +396,28 @@ end end + describe 'read_runner_upgrade_status' do + it { is_expected.to be_disallowed(:read_runner_upgrade_status) } + + context 'when runner_upgrade_management is available' do + before do + stub_licensed_features(runner_upgrade_management: true) + end + + it { is_expected.to be_allowed(:read_runner_upgrade_status) } + end + + context 'when user has paid namespace' do + before do + allow(Gitlab).to receive(:com?).and_return true + group = create(:group_with_plan, plan: :ultimate_plan) + group.add_maintainer(user) + end + + it { expect(described_class.new(user, nil)).to be_allowed(:read_runner_upgrade_status) } + end + end + describe 'admin_service_accounts' do subject { described_class.new(admin, [user]) } diff --git a/ee/spec/requests/api/graphql/ci/runner_spec.rb b/ee/spec/requests/api/graphql/ci/runner_spec.rb index 8dae521af7346..bb63c079e6957 100644 --- a/ee/spec/requests/api/graphql/ci/runner_spec.rb +++ b/ee/spec/requests/api/graphql/ci/runner_spec.rb @@ -10,7 +10,14 @@ shared_examples 'runner details fetch operation returning expected upgradeStatus' do let(:query) do - wrap_fields(query_graphql_path(query_path, 'id upgradeStatus')) + managers = <<~GRAPHQL + managers{ + nodes { + upgradeStatus + } + } + GRAPHQL + wrap_fields(query_graphql_path(query_path, "id upgradeStatus #{managers}")) end let(:query_path) do @@ -23,7 +30,7 @@ allow_next_instance_of(::Gitlab::Ci::RunnerUpgradeCheck) do |instance| allow(instance).to receive(:check_runner_upgrade_suggestion) .and_return([nil, upgrade_status]) - .once + .twice # once for the runner and another for the runner manager end end @@ -34,6 +41,7 @@ expect(runner_data).not_to be_nil expect(runner_data).to match a_graphql_entity_for(runner, upgrade_status: expected_upgrade_status) + expect(graphql_dig_at(runner_data, :managers, :nodes, 0, :upgrade_status)).to eq(expected_upgrade_status) end context 'when fetching runner releases is disabled' do @@ -54,6 +62,7 @@ describe 'upgradeStatus', :saas do let_it_be(:runner) { create(:ci_runner, description: 'Runner 1', version: '14.1.0', revision: 'a') } + let_it_be(:runner_manager) { create(:ci_runner_machine, runner: runner, version: '14.1.0', revision: 'a') } context 'requested by non-paid user' do let(:current_user) { admin } diff --git a/spec/graphql/types/ci/runner_manager_type_spec.rb b/spec/graphql/types/ci/runner_manager_type_spec.rb index 240e1edbf78bc..6f73171cd8f88 100644 --- a/spec/graphql/types/ci/runner_manager_type_spec.rb +++ b/spec/graphql/types/ci/runner_manager_type_spec.rb @@ -13,6 +13,6 @@ runner status system_id version ] - expect(described_class).to have_graphql_fields(*expected_fields) + expect(described_class).to include_graphql_fields(*expected_fields) end end diff --git a/spec/requests/api/graphql/ci/jobs_spec.rb b/spec/requests/api/graphql/ci/jobs_spec.rb index f237516021dbe..756fcd8b7cded 100644 --- a/spec/requests/api/graphql/ci/jobs_spec.rb +++ b/spec/requests/api/graphql/ci/jobs_spec.rb @@ -433,8 +433,6 @@ def all(*fields) end it 'does not generate N+1 queries', :request_store, :use_sql_query_cache do - admin2 = create(:admin) - control = ActiveRecord::QueryRecorder.new(skip_cached: false) do post_graphql(query, current_user: admin) end @@ -442,7 +440,7 @@ def all(*fields) runner_manager2 = create(:ci_runner_machine) create(:ci_build, pipeline: pipeline, name: 'my test job2', runner_manager: runner_manager2) - expect { post_graphql(query, current_user: admin2) }.not_to exceed_all_query_limit(control) + expect { post_graphql(query, current_user: admin) }.not_to exceed_all_query_limit(control) end end -- GitLab