diff --git a/doc/api/graphql/reference/_index.md b/doc/api/graphql/reference/_index.md index 0a102cce2fbba38049b60de85b64fbf773836fd6..03acc8a50a6dc2826f66f589468b67742c24f56d 100644 --- a/doc/api/graphql/reference/_index.md +++ b/doc/api/graphql/reference/_index.md @@ -34178,7 +34178,7 @@ four standard [pagination arguments](#pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | -| `filter` | [`NamespaceClusterAgentFilter!`](#namespaceclusteragentfilter) | Filter the types of cluster agents to return. | +| `filter` | [`OrganizationClusterAgentFilter!`](#organizationclusteragentfilter) | Filter the types of cluster agents to return. | ### `OrganizationStateCounts` @@ -45208,6 +45208,7 @@ Possible filter types for remote development cluster agents in a namespace. | Value | Description | | ----- | ----------- | +| `ALL` | All cluster agents in the namespace that can be used for hosting worksapces. | | `AVAILABLE` | Cluster agents in the namespace that can be used for hosting workspaces. | | `DIRECTLY_MAPPED` | Cluster agents that are directly mapped to the given namespace. | | `UNMAPPED` | Cluster agents within a namespace that are not directly mapped to it. | @@ -45290,6 +45291,15 @@ Enum defining the type of OpenTelemetry metric. | `HISTOGRAM_TYPE` | Histogram Type type. | | `SUM_TYPE` | Sum Type type. | +### `OrganizationClusterAgentFilter` + +Possible filter types for remote development cluster agents in an organization. + +| Value | Description | +| ----- | ----------- | +| `ALL` | All cluster agents in the organization that can be used for hosting workspaces. | +| `DIRECTLY_MAPPED` | Cluster agents that are directly mapped to the given organization. | + ### `OrganizationGroupProjectDisplay` Default list view for organization groups and projects. diff --git a/ee/app/finders/remote_development/namespace_cluster_agents_finder.rb b/ee/app/finders/remote_development/namespace_cluster_agents_finder.rb index 5d899fc46c3de7cf887bebfec962b472e4ff4925..f450b725aba09c60c848d5adbaf3de567609952e 100644 --- a/ee/app/finders/remote_development/namespace_cluster_agents_finder.rb +++ b/ee/app/finders/remote_development/namespace_cluster_agents_finder.rb @@ -18,6 +18,11 @@ def self.execute(namespace:, filter:, user:) # @return [ActiveRecord::Relation] def self.fetch_agents(namespace:, filter:, user:) case filter + when :all + return Clusters::Agent.none unless user_can_read_namespace_agent_mappings?(user: user, namespace: namespace) + + # Returns all agents with remote development enabled + namespace.cluster_agents.with_remote_development_enabled when :unmapped return Clusters::Agent.none unless user_can_read_namespace_agent_mappings?(user: user, namespace: namespace) diff --git a/ee/app/finders/remote_development/organization_cluster_agents_finder.rb b/ee/app/finders/remote_development/organization_cluster_agents_finder.rb index cefac8fdb933ac116cc51485dc892e64d351e3e3..60487e6a9275839ef402f1e44b71f2374d47ff35 100644 --- a/ee/app/finders/remote_development/organization_cluster_agents_finder.rb +++ b/ee/app/finders/remote_development/organization_cluster_agents_finder.rb @@ -10,7 +10,7 @@ def self.execute(organization:, filter:, user:) return Clusters::Agent.none unless organization && user.can?(:read_organization_cluster_agent_mapping, organization) - fetch_agents(filter: filter, organization: organization) + fetch_agents(filter: filter, organization: organization).ordered_by_name end # @param [RemoteDevelopment::Organization] organization @@ -18,17 +18,10 @@ def self.execute(organization:, filter:, user:) # @return [ActiveRecord::Relation] def self.fetch_agents(organization:, filter:) case filter - when :unmapped - # rubocop: disable CodeReuse/ActiveRecord -- activerecord is convenient for filtering for records - # noinspection RailsParamDefResolve -- RubyMine is not finding organization_cluster_agent_mapping - Clusters::Agent.for_organizations([organization.id]) - .left_joins(:organization_cluster_agent_mapping) - .where(organization_cluster_agent_mapping: { id: nil }) - .select('"cluster_agents".*') - # rubocop: enable CodeReuse/ActiveRecord + when :all + # Returns all agents that have remote development enabled + Clusters::Agent.for_organizations([organization.id]).with_remote_development_enabled when :directly_mapped - organization.mapped_agents - when :available organization.mapped_agents.with_remote_development_enabled else raise "Unsupported value for filter: #{filter}" diff --git a/ee/app/graphql/resolvers/remote_development/organization/cluster_agents_resolver.rb b/ee/app/graphql/resolvers/remote_development/organization/cluster_agents_resolver.rb index 5ed91c94f8e23d5c2759d7f4d5c7656033113186..e262ed06927a5cabe796a7858ce0541a5b944456 100644 --- a/ee/app/graphql/resolvers/remote_development/organization/cluster_agents_resolver.rb +++ b/ee/app/graphql/resolvers/remote_development/organization/cluster_agents_resolver.rb @@ -8,7 +8,7 @@ class ClusterAgentsResolver < ::Resolvers::BaseResolver type Types::Clusters::AgentType.connection_type, null: true - argument :filter, Types::RemoteDevelopment::NamespaceClusterAgentFilterEnum, + argument :filter, Types::RemoteDevelopment::OrganizationClusterAgentFilterEnum, required: true, description: 'Filter the types of cluster agents to return.' diff --git a/ee/app/graphql/types/remote_development/namespace_cluster_agent_filter_enum.rb b/ee/app/graphql/types/remote_development/namespace_cluster_agent_filter_enum.rb index 71aead69e0d40d5b6a2aaecba5d6a5a8eff57413..0c73b98e51a9c7552464ccd34b7bafe5fba289d0 100644 --- a/ee/app/graphql/types/remote_development/namespace_cluster_agent_filter_enum.rb +++ b/ee/app/graphql/types/remote_development/namespace_cluster_agent_filter_enum.rb @@ -14,6 +14,9 @@ class NamespaceClusterAgentFilterEnum < BaseEnum value 'UNMAPPED', description: "Cluster agents within a namespace that are not directly mapped to it.", value: 'UNMAPPED' + + value 'ALL', + description: "All cluster agents in the namespace that can be used for hosting worksapces.", value: 'ALL' end end end diff --git a/ee/app/graphql/types/remote_development/organization_cluster_agent_filter_enum.rb b/ee/app/graphql/types/remote_development/organization_cluster_agent_filter_enum.rb new file mode 100644 index 0000000000000000000000000000000000000000..63d6be4e004a1f0e7bf44c6975b00f8011345656 --- /dev/null +++ b/ee/app/graphql/types/remote_development/organization_cluster_agent_filter_enum.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Types + module RemoteDevelopment + class OrganizationClusterAgentFilterEnum < BaseEnum + graphql_name 'OrganizationClusterAgentFilter' + description 'Possible filter types for remote development cluster agents in an organization' + + value 'DIRECTLY_MAPPED', + description: "Cluster agents that are directly mapped to the given organization.", value: 'DIRECTLY_MAPPED' + + value 'ALL', + description: "All cluster agents in the organization that can be used for hosting workspaces.", value: 'ALL' + end + end +end diff --git a/ee/spec/finders/remote_development/namespace_cluster_agents_finder_spec.rb b/ee/spec/finders/remote_development/namespace_cluster_agents_finder_spec.rb index 04645897338c21a48f1d7cd6666009832ee7d859..4d6611566aff88e58b891f15f95a4091210c78f5 100644 --- a/ee/spec/finders/remote_development/namespace_cluster_agents_finder_spec.rb +++ b/ee/spec/finders/remote_development/namespace_cluster_agents_finder_spec.rb @@ -81,6 +81,14 @@ ).to_a end + shared_examples 'when user does not have adequate permissions' do + let(:user) { developer } + + it 'returns an empty response' do + expect(response).to eq([]) + end + end + context 'with filter_type set to available' do context 'when cluster agents are mapped to the namespace' do it 'returns cluster agents mapped to the namespace excluding those with remote dev disabled' do @@ -129,13 +137,7 @@ end end - context 'when user does not have adequate permissions' do - let(:user) { developer } - - it 'returns an empty response' do - expect(response).to eq([]) - end - end + it_behaves_like 'when user does not have adequate permissions' end context 'with filter_type set to unmapped' do @@ -156,13 +158,18 @@ end end - context 'when user does not have adequate permissions' do - let(:user) { developer } + it_behaves_like 'when user does not have adequate permissions' + end - it 'returns an empty response' do - expect(response).to eq([]) - end + context 'with filter_type set to all' do + let(:filter) { :all } + let_it_be(:user) { maintainer } + + it 'returns all cluster agents within a namespace' do + expect(response).to match_array([root_agent, nested_agent, unmapped_root_agent]) end + + it_behaves_like 'when user does not have adequate permissions' end context 'with an invalid value for filter_type' do diff --git a/ee/spec/finders/remote_development/organization_cluster_agents_finder_spec.rb b/ee/spec/finders/remote_development/organization_cluster_agents_finder_spec.rb index 3955829efe8c5d3292d6cbe4081895141171e50f..bb3038e727c5f068a992ef3d90b83c7fc913669b 100644 --- a/ee/spec/finders/remote_development/organization_cluster_agents_finder_spec.rb +++ b/ee/spec/finders/remote_development/organization_cluster_agents_finder_spec.rb @@ -12,7 +12,7 @@ end end - let(:filter) { :available } + let(:filter) { :directly_mapped } let_it_be(:mapped_agent_in_org) do project = create(:project, organization: organization, namespace: create(:group)) @@ -29,14 +29,6 @@ end end - let_it_be(:agent_in_org_with_remote_dev_disabled) do - project = create(:project, organization: organization) - create(:ee_cluster_agent, project: project, name: "agent-in-org-unavailable").tap do |agent| - create(:workspaces_agent_config, agent: agent, enabled: false) - create(:organization_cluster_agent_mapping, user: user, agent: agent, organization: organization) - end - end - describe '#execute' do subject(:response) do # noinspection RubyMismatchedArgumentType -- We are passing a test double @@ -47,27 +39,9 @@ ).to_a end - context 'with filter_type set to available' do - context 'when cluster agents are mapped to the organization' do - it 'returns cluster agents mapped to the organization excluding those with remote dev disabled' do - expect(response).to eq([mapped_agent_in_org]) - end - end - end - context 'with filter_type set to directly_mapped' do - let(:filter) { :directly_mapped } - it 'returns cluster agents that are mapped directly to the organization, including those disabled' do - expect(response).to match_array([mapped_agent_in_org, agent_in_org_with_remote_dev_disabled]) - end - end - - context 'with filter_type set to unmapped' do - let(:filter) { :unmapped } - - it 'returns cluster agents that are unmapped to the organization' do - expect(response).to eq([unmapped_agent_in_org]) + expect(response).to match_array([mapped_agent_in_org]) end end @@ -79,6 +53,14 @@ end end + context 'with filter_type set to all' do + let(:filter) { :all } + + it "returns all cluster agents with remote development enabled within the organization" do + expect(response).to match_array([mapped_agent_in_org, unmapped_agent_in_org]) + end + end + context 'with an invalid value for filter_type' do let(:filter) { "some_invalid_value" } diff --git a/ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/shared.rb b/ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/shared.rb index d72e5dbed48bf0db6a8a7b9690a0d4febdc91b55..53d25f75e4a356d773fe595863bd69684b3c613e 100644 --- a/ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/shared.rb +++ b/ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/shared.rb @@ -16,7 +16,7 @@ let_it_be(:unauthorized_user) { create(:user) } - let_it_be(:available_agent) do + let_it_be(:mapped_agent) do project = create(:project, organization: organization, namespace: create(:group)) create(:ee_cluster_agent, project: project, name: "agent-in-org-available").tap do |agent| create(:workspaces_agent_config, agent: agent) @@ -24,14 +24,6 @@ end end - let_it_be(:directly_mapped_but_disabled_agent) do - project = create(:project, organization: organization) - create(:ee_cluster_agent, project: project, name: "agent-in-org-mapped").tap do |agent| - create(:workspaces_agent_config, agent: agent, enabled: false) - create(:organization_cluster_agent_mapping, user: authorized_user, agent: agent, organization: organization) - end - end - let_it_be(:unmapped_agent) do project = create(:project, organization: organization) create(:ee_cluster_agent, project: project, name: "agent-in-org-unmapped").tap do |agent| @@ -109,15 +101,13 @@ end # noinspection RubyArgCount -- Rubymine detecting wrong types, thinks some #create are from Minitest, not FactoryBot - context "when the user is authorized only on mapped and available agents" do + context "when the user is authorized only on mapped agents" do let_it_be(:current_user) do create(:user).tap do |u| create(:organization_user, organization: organization, user: u) end end - let_it_be(:unavailable_and_unmapped_agents) { [unmapped_agent.name, directly_mapped_but_disabled_agent.name] } - it_behaves_like "query is a working graphql query" context "when the user requests agents" do @@ -126,7 +116,7 @@ end it "does not include unmapped and unavailable agents", :unlimited_max_formatted_output_length do - expect(agent_names.sort).not_to include(*unavailable_and_unmapped_agents) + expect(agent_names.sort).not_to include(unmapped_agent.name) end end end diff --git a/ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_available_filter_arg_spec.rb b/ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_all_filter_arg_spec.rb similarity index 73% rename from ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_available_filter_arg_spec.rb rename to ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_all_filter_arg_spec.rb index bf430daee066e2b21f5c78f14874662542f5b019..bae3dabe1cc3a1dd3ea0dfc28b47fc8e476a6e68 100644 --- a/ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_available_filter_arg_spec.rb +++ b/ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_all_filter_arg_spec.rb @@ -3,9 +3,9 @@ require "spec_helper" require_relative "./shared" -RSpec.describe "Query.organization.workspaces_cluster_agents(filter: AVAILABLE)", feature_category: :workspaces do - let(:filter) { :AVAILABLE } - let(:expected_agents) { [available_agent] } +RSpec.describe "Query.organization.workspaces_cluster_agents(filter: ALL)", feature_category: :workspaces do + let(:filter) { :ALL } + let(:expected_agents) { [mapped_agent, unmapped_agent] } include_context "with agents and users setup in an organization" include_context "for a Query.organization.workspaces_cluster_agents query" diff --git a/ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_directly_mapped_filter_arg_spec.rb b/ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_directly_mapped_filter_arg_spec.rb index 4a7504fe7ba79ba29f2fd26ef366d6b39def3aa7..a0dafbd50d8f7656707453880dc16f2a29fa300e 100644 --- a/ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_directly_mapped_filter_arg_spec.rb +++ b/ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_directly_mapped_filter_arg_spec.rb @@ -5,7 +5,7 @@ RSpec.describe "Query.organization.workspaces_cluster_agents (filter: DIRECTLY_MAPPED)", feature_category: :workspaces do let(:filter) { :DIRECTLY_MAPPED } - let(:expected_agents) { [directly_mapped_but_disabled_agent, available_agent] } + let(:expected_agents) { [mapped_agent] } include_context "with agents and users setup in an organization" include_context "for a Query.organization.workspaces_cluster_agents query" diff --git a/ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_unmapped_filter_arg_spec.rb b/ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_unmapped_filter_arg_spec.rb deleted file mode 100644 index f87b0431a78d5c7ff030842361db7657813ce94c..0000000000000000000000000000000000000000 --- a/ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_unmapped_filter_arg_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require_relative './shared' - -RSpec.describe 'Query.organization.workspaces_cluster_agents (filter: UNMAPPED)', feature_category: :workspaces do - let(:filter) { :UNMAPPED } - let(:expected_agents) { [unmapped_agent] } - - include_context "with agents and users setup in an organization" - include_context "for a Query.organization.workspaces_cluster_agents query" - - it_behaves_like "multiple agents in organization query" -end diff --git a/scripts/verify-tff-mapping b/scripts/verify-tff-mapping index 76a741bd0ce7b6b5eee65a0dea0779289e357aa2..85615d94159cb4185524dde3329703f9ef131cbb 100755 --- a/scripts/verify-tff-mapping +++ b/scripts/verify-tff-mapping @@ -534,7 +534,7 @@ tests = [ changed_file: 'ee/app/graphql/resolvers/remote_development/organization/cluster_agents_resolver.rb', # rubocop:disable Layout/LineLength -- fix CI failures - not sure why other lines in this file don't get errors expected: %w[ - ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_available_filter_arg_spec.rb + ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_all_filter_arg_spec.rb ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_directly_mapped_filter_arg_spec.rb ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_unmapped_filter_arg_spec.rb ]