From 55ccd81a580e8082f503de5192346423b4dcca01 Mon Sep 17 00:00:00 2001
From: Pedro Pombeiro <noreply@pedro.pombei.ro>
Date: Tue, 12 Apr 2022 09:35:25 +0000
Subject: [PATCH] GraphQL: Implement CiRunnerType.upgradeStatus

Add `CiRunnerUpgradeStatusType` to allow determining what type of upgrade
can be applied to a runner
---
 .../ci/runner_upgrade_status_type_enum.rb     | 21 ++++++
 doc/api/graphql/reference/index.md            |  9 +++
 ee/app/graphql/ee/types/ci/runner_type.rb     |  8 +++
 ee/spec/graphql/types/ci/runner_type_spec.rb  |  2 +-
 .../requests/api/graphql/ci/runner_spec.rb    | 64 +++++++++++++++++++
 spec/requests/api/graphql/ci/runner_spec.rb   |  4 ++
 spec/requests/api/graphql/ci/runners_spec.rb  |  2 +
 7 files changed, 109 insertions(+), 1 deletion(-)
 create mode 100644 app/graphql/types/ci/runner_upgrade_status_type_enum.rb
 create mode 100644 ee/spec/requests/api/graphql/ci/runner_spec.rb

diff --git a/app/graphql/types/ci/runner_upgrade_status_type_enum.rb b/app/graphql/types/ci/runner_upgrade_status_type_enum.rb
new file mode 100644
index 000000000000..e3d77e485bc6
--- /dev/null
+++ b/app/graphql/types/ci/runner_upgrade_status_type_enum.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Types
+  module Ci
+    class RunnerUpgradeStatusTypeEnum < BaseEnum
+      graphql_name 'CiRunnerUpgradeStatusType'
+
+      value 'NOT_AVAILABLE',
+            description: "An update is not available for the runner.",
+            value: :not_available
+
+      value 'AVAILABLE',
+            description: "An update is available for the runner.",
+            value: :available
+
+      value 'RECOMMENDED',
+            description: "An update is available and recommended for the runner.",
+            value: :recommended
+    end
+  end
+end
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index f1cdf46625d1..879d836bc7a8 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -9450,6 +9450,7 @@ Represents the total number of issues and their weights for a particular day.
 | <a id="cirunnershortsha"></a>`shortSha` | [`String`](#string) | First eight characters of the runner's token used to authenticate new job requests. Used as the runner's unique ID. |
 | <a id="cirunnertaglist"></a>`tagList` | [`[String!]`](#string) | Tags associated with the runner. |
 | <a id="cirunnertokenexpiresat"></a>`tokenExpiresAt` | [`Time`](#time) | Runner token expiration time. |
+| <a id="cirunnerupgradestatus"></a>`upgradeStatus` **{warning-solid}** | [`CiRunnerUpgradeStatusType`](#cirunnerupgradestatustype) | **Deprecated** in 14.10. This feature is in Alpha, and can be removed or changed at any point. |
 | <a id="cirunneruserpermissions"></a>`userPermissions` | [`RunnerPermissions!`](#runnerpermissions) | Permissions for the current user on the resource. |
 | <a id="cirunnerversion"></a>`version` | [`String`](#string) | Version of the runner. |
 
@@ -17841,6 +17842,14 @@ Values for sorting runners.
 | <a id="cirunnertypeinstance_type"></a>`INSTANCE_TYPE` | A runner that is instance type. |
 | <a id="cirunnertypeproject_type"></a>`PROJECT_TYPE` | A runner that is project type. |
 
+### `CiRunnerUpgradeStatusType`
+
+| Value | Description |
+| ----- | ----------- |
+| <a id="cirunnerupgradestatustypeavailable"></a>`AVAILABLE` | An update is available for the runner. |
+| <a id="cirunnerupgradestatustypenot_available"></a>`NOT_AVAILABLE` | An update is not available for the runner. |
+| <a id="cirunnerupgradestatustyperecommended"></a>`RECOMMENDED` | An update is available and recommended for the runner. |
+
 ### `CodeQualityDegradationSeverity`
 
 | Value | Description |
diff --git a/ee/app/graphql/ee/types/ci/runner_type.rb b/ee/app/graphql/ee/types/ci/runner_type.rb
index ae59b74bcf81..d882353b4101 100644
--- a/ee/app/graphql/ee/types/ci/runner_type.rb
+++ b/ee/app/graphql/ee/types/ci/runner_type.rb
@@ -11,6 +11,14 @@ module RunnerType
                 description: 'Public projects\' "minutes cost factor" associated with the runner (GitLab.com only).'
           field :private_projects_minutes_cost_factor, GraphQL::Types::Float, null: true,
                 description: 'Private projects\' "minutes cost factor" associated with the runner (GitLab.com only).'
+
+          field :upgrade_status, ::Types::Ci::RunnerUpgradeStatusTypeEnum, null: true,
+                description: 'Availability of upgrades for the runner.',
+                deprecated: { milestone: '14.10', reason: :alpha }
+
+          def upgrade_status
+            ::Gitlab::Ci::RunnerUpgradeCheck.instance.check_runner_upgrade_status(object.version)
+          end
         end
       end
     end
diff --git a/ee/spec/graphql/types/ci/runner_type_spec.rb b/ee/spec/graphql/types/ci/runner_type_spec.rb
index 9d986ac9d13e..999e5024ec93 100644
--- a/ee/spec/graphql/types/ci/runner_type_spec.rb
+++ b/ee/spec/graphql/types/ci/runner_type_spec.rb
@@ -6,7 +6,7 @@
   it { expect(described_class.graphql_name).to eq('CiRunner') }
 
   it 'includes the ee specific fields' do
-    expected_fields = %w[public_projects_minutes_cost_factor private_projects_minutes_cost_factor]
+    expected_fields = %w[public_projects_minutes_cost_factor private_projects_minutes_cost_factor upgrade_status]
 
     expect(described_class).to include_graphql_fields(*expected_fields)
   end
diff --git a/ee/spec/requests/api/graphql/ci/runner_spec.rb b/ee/spec/requests/api/graphql/ci/runner_spec.rb
new file mode 100644
index 000000000000..3e954695d0c0
--- /dev/null
+++ b/ee/spec/requests/api/graphql/ci/runner_spec.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Query.runner(id)' do
+  include GraphqlHelpers
+
+  let_it_be(:user) { create(:user, :admin) }
+
+  shared_examples 'runner details fetch operation returning expected upgradeStatus' do
+    let(:query) do
+      wrap_fields(query_graphql_path(query_path, all_graphql_fields_for('CiRunner')))
+    end
+
+    let(:query_path) do
+      [
+        [:runner, { id: runner.to_global_id.to_s }]
+      ]
+    end
+
+    it 'retrieves expected fields' do
+      post_graphql(query, current_user: user)
+
+      runner_data = graphql_data_at(:runner)
+      expect(runner_data).not_to be_nil
+
+      expect(runner_data).to match a_hash_including(
+        'id' => runner.to_global_id.to_s,
+        'upgradeStatus' => expected_upgrade_status
+      )
+    end
+  end
+
+  describe 'upgradeStatus' do
+    let_it_be(:runner) { create(:ci_runner, description: 'Runner 1', version: 'adfe156', revision: 'a') }
+
+    before do
+      expect(::Gitlab::Ci::RunnerUpgradeCheck.instance).to receive(:check_runner_upgrade_status)
+        .and_return(upgrade_status)
+        .once
+    end
+
+    context 'with RunnerUpgradeCheck returning :not_available' do
+      let(:upgrade_status) { :not_available }
+      let(:expected_upgrade_status) { 'NOT_AVAILABLE' }
+
+      it_behaves_like('runner details fetch operation returning expected upgradeStatus')
+    end
+
+    context 'with RunnerUpgradeCheck returning :available' do
+      let(:upgrade_status) { :available }
+      let(:expected_upgrade_status) { 'AVAILABLE' }
+
+      it_behaves_like('runner details fetch operation returning expected upgradeStatus')
+    end
+
+    context 'with RunnerUpgradeCheck returning :recommended' do
+      let(:upgrade_status) { :recommended }
+      let(:expected_upgrade_status) { 'RECOMMENDED' }
+
+      it_behaves_like('runner details fetch operation returning expected upgradeStatus')
+    end
+  end
+end
diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb
index 6102e15012b9..39f0f696b081 100644
--- a/spec/requests/api/graphql/ci/runner_spec.rb
+++ b/spec/requests/api/graphql/ci/runner_spec.rb
@@ -27,6 +27,10 @@
 
   let_it_be(:active_project_runner) { create(:ci_runner, :project) }
 
+  before do
+    allow(Gitlab::Ci::RunnerUpgradeCheck.instance).to receive(:check_runner_upgrade_status)
+  end
+
   shared_examples 'runner details fetch' do
     let(:query) do
       wrap_fields(query_graphql_path(query_path, all_graphql_fields_for('CiRunner')))
diff --git a/spec/requests/api/graphql/ci/runners_spec.rb b/spec/requests/api/graphql/ci/runners_spec.rb
index 267dd1b5e6fd..6b88c82b025c 100644
--- a/spec/requests/api/graphql/ci/runners_spec.rb
+++ b/spec/requests/api/graphql/ci/runners_spec.rb
@@ -34,6 +34,8 @@
     end
 
     before do
+      allow(Gitlab::Ci::RunnerUpgradeCheck.instance).to receive(:check_runner_upgrade_status)
+
       post_graphql(query, current_user: current_user)
     end
 
-- 
GitLab