Commit 58450269 authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch 'pedropombeiro/328549/runner-graphql-mutation' into 'master'

Implement GraphQL Mutation to modify runner details

See merge request gitlab-org/gitlab!61813
parents 995e0080 78d654f9
# frozen_string_literal: true
module Mutations
module Ci
module Runner
class Update < BaseMutation
graphql_name 'RunnerUpdate'
authorize :update_runner
RunnerID = ::Types::GlobalIDType[::Ci::Runner]
argument :id, RunnerID,
required: true,
description: 'ID of the runner to update.'
argument :description, GraphQL::STRING_TYPE,
required: false,
description: 'Description of the runner.'
argument :maximum_timeout, GraphQL::INT_TYPE,
required: false,
description: 'Maximum timeout (in seconds) for jobs processed by the runner.'
argument :access_level, ::Types::Ci::RunnerAccessLevelEnum,
required: false,
description: 'Access level of the runner.'
argument :active, GraphQL::BOOLEAN_TYPE,
required: false,
description: 'Indicates the runner is allowed to receive jobs.'
argument :locked, GraphQL::BOOLEAN_TYPE, required: false,
description: 'Indicates the runner is locked.'
argument :run_untagged, GraphQL::BOOLEAN_TYPE,
required: false,
description: 'Indicates the runner is able to run untagged jobs.'
argument :tag_list, [GraphQL::STRING_TYPE], required: false,
description: 'Tags associated with the runner.'
field :runner,
Types::Ci::RunnerType,
null: true,
description: 'The runner after mutation.'
def resolve(id:, **runner_attrs)
runner = authorized_find!(id)
unless ::Ci::UpdateRunnerService.new(runner).update(runner_attrs)
return { runner: nil, errors: runner.errors.full_messages }
end
{ runner: runner, errors: [] }
end
def find_object(id)
# TODO: remove this line when the compatibility layer is removed
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
id = RunnerID.coerce_isolated_input(id)
GitlabSchema.find_by_gid(id)
end
end
end
end
end
...@@ -99,6 +99,7 @@ module Types ...@@ -99,6 +99,7 @@ module Types
mount_mutation Mutations::Ci::CiCdSettingsUpdate mount_mutation Mutations::Ci::CiCdSettingsUpdate
mount_mutation Mutations::Ci::Job::Play mount_mutation Mutations::Ci::Job::Play
mount_mutation Mutations::Ci::Job::Retry mount_mutation Mutations::Ci::Job::Retry
mount_mutation Mutations::Ci::Runner::Update, feature_flag: :runner_graphql_query
mount_mutation Mutations::Namespace::PackageSettings::Update mount_mutation Mutations::Namespace::PackageSettings::Update
mount_mutation Mutations::UserCallouts::Create mount_mutation Mutations::UserCallouts::Create
end end
......
...@@ -3568,6 +3568,34 @@ Input type: `RunDASTScanInput` ...@@ -3568,6 +3568,34 @@ Input type: `RunDASTScanInput`
| <a id="mutationrundastscanerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | | <a id="mutationrundastscanerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationrundastscanpipelineurl"></a>`pipelineUrl` | [`String`](#string) | URL of the pipeline that was created. | | <a id="mutationrundastscanpipelineurl"></a>`pipelineUrl` | [`String`](#string) | URL of the pipeline that was created. |
### `Mutation.runnerUpdate`
Available only when feature flag `runner_graphql_query` is enabled.
Input type: `RunnerUpdateInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationrunnerupdateaccesslevel"></a>`accessLevel` | [`CiRunnerAccessLevel`](#cirunneraccesslevel) | Access level of the runner. |
| <a id="mutationrunnerupdateactive"></a>`active` | [`Boolean`](#boolean) | Indicates the runner is allowed to receive jobs. |
| <a id="mutationrunnerupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationrunnerupdatedescription"></a>`description` | [`String`](#string) | Description of the runner. |
| <a id="mutationrunnerupdateid"></a>`id` | [`CiRunnerID!`](#cirunnerid) | ID of the runner to update. |
| <a id="mutationrunnerupdatelocked"></a>`locked` | [`Boolean`](#boolean) | Indicates the runner is locked. |
| <a id="mutationrunnerupdatemaximumtimeout"></a>`maximumTimeout` | [`Int`](#int) | Maximum timeout (in seconds) for jobs processed by the runner. |
| <a id="mutationrunnerupdaterununtagged"></a>`runUntagged` | [`Boolean`](#boolean) | Indicates the runner is able to run untagged jobs. |
| <a id="mutationrunnerupdatetaglist"></a>`tagList` | [`[String!]`](#string) | Tags associated with the runner. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationrunnerupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationrunnerupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationrunnerupdaterunner"></a>`runner` | [`CiRunner`](#cirunner) | The runner after mutation. |
### `Mutation.terraformStateDelete` ### `Mutation.terraformStateDelete`
Input type: `TerraformStateDeleteInput` Input type: `TerraformStateDeleteInput`
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::Ci::Runner::Update do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
let_it_be(:runner) { create(:ci_runner, active: true, locked: false, run_untagged: true) }
let(:current_ctx) { { current_user: user } }
let(:mutated_runner) { subject[:runner] }
let(:mutation_params) do
{
id: runner.to_global_id,
description: 'updated description'
}
end
specify { expect(described_class).to require_graphql_authorizations(:update_runner) }
describe '#resolve' do
subject do
sync(resolve(described_class, args: mutation_params, ctx: current_ctx))
end
context 'when the user cannot admin the runner' do
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'with invalid params' do
it 'raises an error' do
mutation_params[:id] = "invalid-id"
expect { subject }.to raise_error(::GraphQL::CoercionError)
end
end
context 'when required arguments are missing' do
let(:mutation_params) { {} }
it 'raises an error' do
expect { subject }.to raise_error(ArgumentError, "missing keyword: :id")
end
end
context 'when user can update runner', :enable_admin_mode do
let(:admin_user) { create(:user, :admin) }
let(:current_ctx) { { current_user: admin_user } }
let(:mutation_params) do
{
id: runner.to_global_id,
description: 'updated description',
maximum_timeout: 900,
access_level: 'ref_protected',
active: false,
locked: true,
run_untagged: false,
tag_list: %w(tag1 tag2)
}
end
context 'with valid arguments' do
it 'updates runner with correct values' do
expected_attributes = mutation_params.except(:id)
subject
expect(subject[:errors]).to be_empty
expect(subject[:runner]).to be_an_instance_of(Ci::Runner)
expect(subject[:runner]).to have_attributes(expected_attributes)
expect(runner.reload).to have_attributes(expected_attributes)
end
end
context 'with out-of-range maximum_timeout and missing tag_list' do
it 'returns a descriptive error' do
mutation_params[:maximum_timeout] = 100
mutation_params.delete(:tag_list)
expect(subject[:errors]).to contain_exactly(
'Maximum timeout needs to be at least 10 minutes',
'Tags list can not be empty when runner is not allowed to pick untagged jobs'
)
end
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment