BE: Update query API to support listing of directly MAPPED / UNMAPPED cluster agents in RD
MR: BE: Support mapped/unmapped agents for new auth... (!151317 - merged)
Description
Change will be made to the resolver of the field remoteDevelopmentClusterAgents
under the namespace
field as technical design for the new group-agent mapping feature.
This updated API will support the following additional enumeration values for its filter
paramter with the following expected behavior:
-
DIRECTLY_MAPPED
: Returns all agents directly mapped to the group -
UNMAPPED
: Returns all agents under the group that are not mapped to the agent
All agents with remote development support must be returned in the API if filter
param is not set.
Acceptance Criteria
-
Implement new API w/ tests as mentioned in the design doc. These include -
Ensure that the APIs ensure the permissions model as described in the doc -
Ensure that the new APIs/changes to the existing API are behind the feature flag as mentioned in the doc
Technical Requirements
- Same as the acceptance criteria
Design Requirements
NA
Impact Assessment
- There should be no impact as the new API should be behind a feature flag
Query plan
Expand for more details
Since the API supports to distinct filter types that cannot be used at the same time, their query plans have been evaluated independently:Fetching agents when filter == UNMAPPED
This is an admin API and its usage is expected to be very limited. The below snippet illustrates the SQL calls that are made when the core invocations from the finder are executed in IRB:
[10] pry(main)> existing_mapped_agents = RemoteDevelopment::RemoteDevelopmentNamespaceClusterAgentMapping.for_namespaces([24]).map(&:cluster_agent_id)
RemoteDevelopment::RemoteDevelopmentNamespaceClusterAgentMapping Load (1.6ms) SELECT "remote_development_namespace_cluster_agent_mappings".* FROM "remote_development_namespace_cluster_agent_mappings" WHERE "remote_development_namespace_cluster_agent_mappings"."namespace_id" = 24 /*application:console,db_config_name:main,console_hostname:Hunars-MacBook-Pro.local,console_username:hkhanna,line:(pry):10:in `map'*/
=> [2]
[11] pry(main)> Namespace.find_by(path: "gitlab-org").cluster_agents.id_not_in(existing_mapped_agents)
Namespace Load (0.9ms) SELECT "namespaces"."id", "namespaces"."name", "namespaces"."path", "namespaces"."owner_id", "namespaces"."created_at", "namespaces"."updated_at", "namespaces"."type", "namespaces"."description", "namespaces"."avatar", "namespaces"."membership_lock", "namespaces"."share_with_group_lock", "namespaces"."visibility_level", "namespaces"."request_access_enabled", "namespaces"."ldap_sync_status", "namespaces"."ldap_sync_error", "namespaces"."ldap_sync_last_update_at", "namespaces"."ldap_sync_last_successful_update_at", "namespaces"."ldap_sync_last_sync_at", "namespaces"."description_html", "namespaces"."lfs_enabled", "namespaces"."parent_id", "namespaces"."shared_runners_minutes_limit", "namespaces"."repository_size_limit", "namespaces"."require_two_factor_authentication", "namespaces"."two_factor_grace_period", "namespaces"."cached_markdown_version", "namespaces"."project_creation_level", "namespaces"."runners_token", "namespaces"."file_template_project_id", "namespaces"."saml_discovery_token", "namespaces"."runners_token_encrypted", "namespaces"."custom_project_templates_group_id", "namespaces"."auto_devops_enabled", "namespaces"."extra_shared_runners_minutes_limit", "namespaces"."last_ci_minutes_notification_at", "namespaces"."last_ci_minutes_usage_notification_level", "namespaces"."subgroup_creation_level", "namespaces"."max_pages_size", "namespaces"."max_artifacts_size", "namespaces"."mentions_disabled", "namespaces"."default_branch_protection", "namespaces"."max_personal_access_token_lifetime", "namespaces"."push_rule_id", "namespaces"."shared_runners_enabled", "namespaces"."allow_descendants_override_disabled_shared_runners", "namespaces"."traversal_ids", "namespaces"."organization_id" FROM "namespaces" WHERE "namespaces"."path" = 'gitlab-org' LIMIT 1 /*application:console,db_config_name:main,console_hostname:Hunars-MacBook-Pro.local,console_username:hkhanna,line:(pry):11:in `__pry__'*/
Clusters::Agent Load (1.9ms) SELECT "cluster_agents".* FROM "cluster_agents" WHERE "cluster_agents"."project_id" IN (SELECT "projects"."id" FROM "projects" WHERE "projects"."namespace_id" IN (SELECT namespaces.traversal_ids[array_length(namespaces.traversal_ids, 1)] AS id FROM "namespaces" WHERE "namespaces"."type" = 'Group' AND (traversal_ids @> ('{24}')))) AND "cluster_agents"."id" != 2 /*application:console,db_config_name:main,console_hostname:Hunars-MacBook-Pro.local,console_username:hkhanna,line:bin/rails:4:in `<main>'*/
=> [#<Clusters::Agent:0x000000016b3d21d0 id: 4, created_at: Thu, 22 Jun 2023 07:29:45.345253000 UTC +00:00, updated_at: Thu, 22 Jun 2023 07:29:45.345253000 UTC +00:00, project_id: 3, name: "something-else", created_by_user_id: 1, has_vulnerabilities: false>,
#<Clusters::Agent:0x000000016b3d2130 id: 3, created_at: Wed, 01 Mar 2023 07:08:58.521503000 UTC +00:00, updated_at: Wed, 01 Mar 2023 07:08:58.521503000 UTC +00:00, project_id: 3, name: "test-agent-2", created_by_user_id: 1, has_vulnerabilities: false>,
#<Clusters::Agent:0x000000016b3d2090 id: 10, created_at: Thu, 28 Mar 2024 03:46:46.897684000 UTC +00:00, updated_at: Thu, 28 Mar 2024 03:46:46.897684000 UTC +00:00, project_id: 3, name: "test-agent-bug", created_by_user_id: 1, has_vulnerabilities: false>]
Fetching agents when filter == DIRECTLY_MAPPED
The series of queries for this are exactly the same as for when the filter is AVAILABLE
, except that the first query i.e. RemoteDevelopmentNamespaceClusterAgentMapping.for_namespaces()
is made for a single namespace_id whereas when the filter is AVAILABLE
, its made for a list of namespace ids.
So, it can only return fewer records than when filter is AVAILABLE
for the same namespace. Furthermore, this is an admin API and its usage is expected to be very limited.