Commit 64a85024 authored by Emily Ring's avatar Emily Ring Committed by Nick Thomas

Cluster Agent delete mutation for GraphQl

Added cluster agent delete mutation fo graphql
Added cluster agent delete service
Updated associated tests
parent fc62bfa5
...@@ -1655,6 +1655,41 @@ type ClusterAgent { ...@@ -1655,6 +1655,41 @@ type ClusterAgent {
updatedAt: Time updatedAt: Time
} }
"""
Autogenerated input type of ClusterAgentDelete
"""
input ClusterAgentDeleteInput {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Global id of the cluster agent that will be deleted
"""
id: ClustersAgentID!
}
"""
Autogenerated return type of ClusterAgentDelete
"""
type ClusterAgentDeletePayload {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Errors encountered during execution of the mutation.
"""
errors: [String!]!
}
"""
Identifier of Clusters::Agent
"""
scalar ClustersAgentID
type Commit { type Commit {
""" """
Author of the commit Author of the commit
...@@ -9591,6 +9626,7 @@ type Mutation { ...@@ -9591,6 +9626,7 @@ type Mutation {
awardEmojiToggle(input: AwardEmojiToggleInput!): AwardEmojiTogglePayload awardEmojiToggle(input: AwardEmojiToggleInput!): AwardEmojiTogglePayload
boardListCreate(input: BoardListCreateInput!): BoardListCreatePayload boardListCreate(input: BoardListCreateInput!): BoardListCreatePayload
boardListUpdateLimitMetrics(input: BoardListUpdateLimitMetricsInput!): BoardListUpdateLimitMetricsPayload boardListUpdateLimitMetrics(input: BoardListUpdateLimitMetricsInput!): BoardListUpdateLimitMetricsPayload
clusterAgentDelete(input: ClusterAgentDeleteInput!): ClusterAgentDeletePayload
commitCreate(input: CommitCreateInput!): CommitCreatePayload commitCreate(input: CommitCreateInput!): CommitCreatePayload
configureSast(input: ConfigureSastInput!): ConfigureSastPayload configureSast(input: ConfigureSastInput!): ConfigureSastPayload
createAlertIssue(input: CreateAlertIssueInput!): CreateAlertIssuePayload createAlertIssue(input: CreateAlertIssueInput!): CreateAlertIssuePayload
......
...@@ -4518,6 +4518,104 @@ ...@@ -4518,6 +4518,104 @@
"enumValues": null, "enumValues": null,
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "INPUT_OBJECT",
"name": "ClusterAgentDeleteInput",
"description": "Autogenerated input type of ClusterAgentDelete",
"fields": null,
"inputFields": [
{
"name": "id",
"description": "Global id of the cluster agent that will be deleted",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ClustersAgentID",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "ClusterAgentDeletePayload",
"description": "Autogenerated return type of ClusterAgentDelete",
"fields": [
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "errors",
"description": "Errors encountered during execution of the mutation.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "SCALAR",
"name": "ClustersAgentID",
"description": "Identifier of Clusters::Agent",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "OBJECT", "kind": "OBJECT",
"name": "Commit", "name": "Commit",
...@@ -27089,6 +27187,33 @@ ...@@ -27089,6 +27187,33 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "clusterAgentDelete",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "ClusterAgentDeleteInput",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "ClusterAgentDeletePayload",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "commitCreate", "name": "commitCreate",
"description": null, "description": null,
...@@ -274,6 +274,15 @@ Autogenerated return type of BoardListUpdateLimitMetrics ...@@ -274,6 +274,15 @@ Autogenerated return type of BoardListUpdateLimitMetrics
| `project` | Project | The project this cluster agent is associated with | | `project` | Project | The project this cluster agent is associated with |
| `updatedAt` | Time | Timestamp the cluster agent was updated | | `updatedAt` | Time | Timestamp the cluster agent was updated |
## ClusterAgentDeletePayload
Autogenerated return type of ClusterAgentDelete
| Name | Type | Description |
| --- | ---- | ---------- |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
## Commit ## Commit
| Name | Type | Description | | Name | Type | Description |
......
...@@ -7,6 +7,7 @@ module EE ...@@ -7,6 +7,7 @@ module EE
prepended do prepended do
mount_mutation ::Mutations::Clusters::Agents::Create mount_mutation ::Mutations::Clusters::Agents::Create
mount_mutation ::Mutations::Clusters::Agents::Delete
mount_mutation ::Mutations::Issues::SetIteration mount_mutation ::Mutations::Issues::SetIteration
mount_mutation ::Mutations::Issues::SetWeight mount_mutation ::Mutations::Issues::SetWeight
mount_mutation ::Mutations::Issues::SetEpic mount_mutation ::Mutations::Issues::SetEpic
......
# frozen_string_literal: true
module Mutations
module Clusters
module Agents
class Delete < BaseMutation
graphql_name 'ClusterAgentDelete'
authorize :admin_cluster
argument :id,
::Types::GlobalIDType[::Clusters::Agent],
required: true,
description: 'Global id of the cluster agent that will be deleted'
def resolve(id:)
cluster_agent = authorized_find!(id: id)
result = ::Clusters::Agents::DeleteService
.new(container: cluster_agent.project, current_user: current_user)
.execute(cluster_agent)
{
errors: Array.wrap(result.message)
}
end
private
def find_object(id:)
GitlabSchema.find_by_gid(id)
end
end
end
end
end
# frozen_string_literal: true
module Clusters
module Agents
class DeleteService < ::BaseContainerService
def execute(cluster_agent)
return error_no_permissions unless current_user.can?(:admin_cluster, cluster_agent)
if cluster_agent.destroy
ServiceResponse.success
else
ServiceResponse.error(message: cluster_agent.errors.full_messages)
end
end
private
def error_no_permissions
ServiceResponse.error(message: s_('ClusterAgent|You have insufficient permissions to delete this cluster agent'))
end
end
end
end
---
title: Added Cluster Agent delete mutation for GraphQl
merge_request: 38622
author:
type: changed
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::Clusters::Agents::Delete do
subject(:mutation) { described_class.new(object: nil, context: context, field: nil) }
let(:cluster_agent) { create(:cluster_agent) }
let(:project) { cluster_agent.project }
let(:user) { create(:user) }
let(:context) do
GraphQL::Query::Context.new(
query: OpenStruct.new(schema: nil),
values: { current_user: user },
object: nil
)
end
specify { expect(described_class).to require_graphql_authorizations(:admin_cluster) }
describe '#resolve' do
subject { mutation.resolve(id: cluster_agent.to_global_id) }
context 'without user permissions' do
it 'fails to delete the cluster agent', :aggregate_failures do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
expect { cluster_agent.reload }.not_to raise_error(ActiveRecord::RecordNotFound)
end
end
context 'with user permissions' do
before do
project.add_maintainer(user)
end
it 'deletes a cluster agent', :aggregate_failures do
expect { subject }.to change { ::Clusters::Agent.count }.by(-1)
expect { cluster_agent.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end
context 'with invalid params' do
subject { mutation.resolve(id: cluster_agent.id) }
it 'raises an error if the cluster agent id is invalid', :aggregate_failures do
expect { subject }.to raise_error(NoMethodError)
expect { cluster_agent.reload }.not_to raise_error(ActiveRecord::RecordNotFound)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Delete a cluster agent' do
include GraphqlHelpers
let(:cluster_agent) { create(:cluster_agent) }
let(:project) { cluster_agent.project }
let(:current_user) { create(:user) }
let(:mutation) do
graphql_mutation(
:cluster_agent_delete,
{ id: cluster_agent.to_global_id.uri }
)
end
def mutation_response
graphql_mutation_response(:cluster_agent_delete)
end
context 'without project permissions' do
it_behaves_like 'a mutation that returns top-level errors',
errors: ['The resource that you are attempting to access does not exist '\
'or you don\'t have permission to perform this action']
it 'does not delete cluster agent' do
expect { cluster_agent.reload }.not_to raise_error(ActiveRecord::RecordNotFound)
end
end
context 'with premium plan and project permissions' do
before do
allow(License).to receive(:current).and_return(create(:license, plan: ::License::PREMIUM_PLAN))
project.add_maintainer(current_user)
end
it 'deletes a cluster agent', :aggregate_failures do
expect { post_graphql_mutation(mutation, current_user: current_user) }.to change { Clusters::Agent.count }.by(-1)
expect(mutation_response['errors']).to eq([])
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Clusters::Agents::DeleteService do
subject(:service) { described_class.new(container: project, current_user: user) }
let(:cluster_agent) { create(:cluster_agent) }
let(:project) { cluster_agent.project }
let(:user) { create(:user) }
describe '#execute' do
context 'without user permissions' do
it 'fails to delete when the user has no permissions', :aggregate_failures do
response = service.execute(cluster_agent)
expect(response.status).to eq(:error)
expect(response.message).to eq('You have insufficient permissions to delete this cluster agent')
expect { cluster_agent.reload }.not_to raise_error(ActiveRecord::RecordNotFound)
end
end
context 'with user permissions' do
before do
project.add_maintainer(user)
end
it 'deletes a cluster agent', :aggregate_failures do
expect { service.execute(cluster_agent) }.to change { ::Clusters::Agent.count }.by(-1)
expect { cluster_agent.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end
end
end
...@@ -5114,6 +5114,9 @@ msgstr "" ...@@ -5114,6 +5114,9 @@ msgstr ""
msgid "ClusterAgent|You have insufficient permissions to create a cluster agent for this project" msgid "ClusterAgent|You have insufficient permissions to create a cluster agent for this project"
msgstr "" msgstr ""
msgid "ClusterAgent|You have insufficient permissions to delete this cluster agent"
msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster" msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "" msgstr ""
......
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