Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
47c3acc07e
commit
2cec357c4f
|
|
@ -66,21 +66,23 @@ class WorkItem < Issue
|
|||
end
|
||||
|
||||
def work_item_children_keyset_order
|
||||
keyset_order = Gitlab::Pagination::Keyset::Order.build([
|
||||
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
|
||||
attribute_name: :relative_position,
|
||||
column_expression: WorkItems::ParentLink.arel_table[:relative_position],
|
||||
order_expression: WorkItems::ParentLink.arel_table[:relative_position].asc.nulls_last,
|
||||
nullable: :nulls_last
|
||||
),
|
||||
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
|
||||
attribute_name: :created_at,
|
||||
order_expression: WorkItem.arel_table[:created_at].asc,
|
||||
nullable: :not_nullable
|
||||
)
|
||||
])
|
||||
keyset_order = Gitlab::Pagination::Keyset::Order.build(
|
||||
[
|
||||
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
|
||||
attribute_name: 'parent_link_relative_position',
|
||||
column_expression: WorkItems::ParentLink.arel_table[:relative_position],
|
||||
order_expression: WorkItems::ParentLink.arel_table[:relative_position].asc.nulls_last,
|
||||
add_to_projections: true,
|
||||
nullable: :nulls_last
|
||||
),
|
||||
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
|
||||
attribute_name: 'work_item_id',
|
||||
order_expression: WorkItems::ParentLink.arel_table['work_item_id'].asc
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
includes(:parent_link).order(keyset_order)
|
||||
keyset_order.apply_cursor_conditions(includes(:parent_link)).reorder(keyset_order)
|
||||
end
|
||||
|
||||
def linked_items_keyset_order
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
owning-stage: "~devops::create"
|
||||
description: 'GitLab Remote Development ADR 100: New agent authorization strategy'
|
||||
---
|
||||
|
||||
# GitLab Remote Development ADR 001: New agent authorization strategy
|
||||
|
||||
## Context
|
||||
|
||||
A decision was made to drop the legacy agent authorization strategy in favor of the new agent authorization strategy. As covered in [detailed proposal](https://gitlab.com/gitlab-org/remote-development/gitlab-remote-development-docs/-/blob/main/doc/proposal-for-mapping-projects-to-agents.md?ref_type=heads#problems-with-the-current-solution), the current solution has several issues that make it unsuitable for the long-term needs of the GitLab Remote Development feature. The main problems are:
|
||||
|
||||
1. **Limited flexibility**: The legacy agent authorization strategy relies on granting group-level Developer role to potential users. This makes it unsuitable for use in some organisations where users are not granted access at a group level.
|
||||
1. **Potential security risks**: The legacy approach allows any user with Developer role within a limited scope to spin up a GitLab Agent and have it be potentially used for workspaces by users with relevant access to the project. Since workspaces contain privileged information such as secrets, more control should be enforced on what GitLab Agents may be select for hosting workspaces within a given scope (for e.g a group) as it is with GitLab CI Runners.
|
||||
|
||||
## Decision
|
||||
|
||||
Taking inspiration from the authorization model for GitLab CI Runners, a new authorization strategy for GitLab Agents will be introduced. In order understand how workspaces can be created using the new authorization strategy, it's important to understand the following rules:
|
||||
|
||||
- A user can only create workspaces using a cluster agent that is "available", and which has been configured for remote_development.
|
||||
- A user must have Developer role to both the agent project and the workspace project.
|
||||
- An agent is considered "available" for use with a workspace if a group owner or administrator has mapped the cluster agent to any parent group of the workspace project. Another way of looking at it is; a mapping between a cluster agent and a group is inherited by its subgroups.
|
||||
- Mapping between a cluster agent and a group is a new concept that has been introduced with the revamped authorization strategy. A group owner may create a mapping between the group and any cluster agent residing within the group or its subgroup. **NOTE:** By default, no cluster agent is mapped to a group. Additionally, if a project resides within a group, it does NOT imply that the cluster agents of this project are mapped to the parent group(s).
|
||||
|
||||
In addition the above, the first phase of delivery will have the following restrictions:
|
||||
|
||||
- A GitLab Agent may only be mapped to a group. In the future, mapping cluster agents to the instance, user namespaces etc. can/should be explored.
|
||||
- A GitLab Agent may only be mapped to a parent group. The group in question may or may not be a direct parent. For eg. if an agent belongs to a project with path `root-group/nested-group/agent-project`, then the agent may be mapped to either `root-group` and/or `nested-group`. In the future, there may be a need to consider mapping agents to a non-parent group. However, this will increase the scope of the task significantly due to additional considerations: for example, what if some owners/maintainers of a group do not access to the agent being mapped? This is not a problem when the agent is contained within the group. However, this usecase will have to be thought through if such a capability must be supported consistently.
|
||||
|
||||
For more details, on the details of the new authorization strategy, please refer to the [detailed technical design](https://gitlab.com/gitlab-org/remote-development/gitlab-remote-development-docs/-/blob/e28003334fda100295ed41bd84eef2b1770d86af/doc/tech-designs/2024-01-23-support-group-agent-authorization.md).
|
||||
|
||||
## Consequences
|
||||
|
||||
Since the new strategy is incompatible with the legacy authorization strategy, this feature will be put behind a feature flag and rolled out gradually. Additionally, in order to provide a smooth user experience during feature rollout, a one-time data migration will take place to create mappings between root groups and remote development cluster agents within these groups. After this migration, for any changes desired to the list of cluster agents available during workspace creation, users will be required to explicitly create/delete mappings.
|
||||
|
||||
## Alternatives
|
||||
|
||||
NA
|
||||
|
|
@ -12,6 +12,10 @@ participating-stages: []
|
|||
|
||||
# Remote development
|
||||
|
||||
## Decisions
|
||||
|
||||
- [100: New agent authorization strategy](decisions/100_new_agent_authorization_strategy.md)
|
||||
|
||||
## Summary
|
||||
|
||||
Remote development is a new architecture for our software-as-a-service platform that provides a more consistent user experience writing code hosted in GitLab. It may also provide additional features in the future, such as a purely browser-based workspace and the ability to connect to an already running VM/Container or to use a GitLab-hosted VM/Container.
|
||||
|
|
|
|||
|
|
@ -133,6 +133,18 @@ RSpec.describe Gitlab::Usage::EventSelectionRule, feature_category: :service_pin
|
|||
expect(event_selection_rule.redis_keys_for_time_frame('7d'))
|
||||
.to eq(["{event_counters}_an_event-filter:[label:foo]-2024-21"])
|
||||
end
|
||||
|
||||
context 'when key is overridden' do
|
||||
before do
|
||||
stub_file_read(Gitlab::UsageDataCounters::HLLRedisCounter::KEY_OVERRIDES_PATH,
|
||||
content: 'an_event-filter:[label:foo]: a_legacy_key')
|
||||
end
|
||||
|
||||
it 'uses the legacy key' do
|
||||
expect(event_selection_rule.redis_keys_for_time_frame('7d'))
|
||||
.to eq(["{event_counters}_a_legacy_key-2024-21"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when time_frame is "28d"' do
|
||||
|
|
|
|||
|
|
@ -48,9 +48,9 @@ RSpec.describe WorkItem, feature_category: :portfolio_management do
|
|||
subject { parent_item.reload.work_item_children_by_relative_position }
|
||||
|
||||
let_it_be(:parent_item) { create(:work_item, :objective, project: reusable_project) }
|
||||
let_it_be(:oldest_item) { create(:work_item, :objective, created_at: 5.hours.ago, project: reusable_project) }
|
||||
let_it_be(:oldest_item) { create(:work_item, :objective, project: reusable_project) }
|
||||
let_it_be(:middle_item) { create(:work_item, :objective, project: reusable_project) }
|
||||
let_it_be(:newest_item) { create(:work_item, :objective, created_at: 5.hours.from_now, project: reusable_project) }
|
||||
let_it_be(:newest_item) { create(:work_item, :objective, project: reusable_project) }
|
||||
|
||||
let_it_be_with_reload(:link_to_oldest_item) do
|
||||
create(:parent_link, work_item_parent: parent_item, work_item: oldest_item)
|
||||
|
|
@ -64,7 +64,7 @@ RSpec.describe WorkItem, feature_category: :portfolio_management do
|
|||
create(:parent_link, work_item_parent: parent_item, work_item: newest_item)
|
||||
end
|
||||
|
||||
context 'when ordered by relative position and created_at' do
|
||||
context 'when ordered by relative position' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where(:oldest_item_position, :middle_item_position, :newest_item_position, :expected_order) do
|
||||
|
|
@ -73,6 +73,11 @@ RSpec.describe WorkItem, feature_category: :portfolio_management do
|
|||
nil | 1 | 2 | lazy { [middle_item, newest_item, oldest_item] }
|
||||
2 | 3 | 1 | lazy { [newest_item, oldest_item, middle_item] }
|
||||
1 | 2 | 3 | lazy { [oldest_item, middle_item, newest_item] }
|
||||
1 | 3 | 2 | lazy { [oldest_item, newest_item, middle_item] }
|
||||
2 | 1 | 3 | lazy { [middle_item, oldest_item, newest_item] }
|
||||
3 | 1 | 2 | lazy { [middle_item, newest_item, oldest_item] }
|
||||
3 | 2 | 1 | lazy { [newest_item, middle_item, oldest_item] }
|
||||
1 | 2 | 1 | lazy { [oldest_item, newest_item, middle_item] }
|
||||
end
|
||||
|
||||
with_them do
|
||||
|
|
|
|||
|
|
@ -36,9 +36,9 @@ RSpec.describe WorkItems::Widgets::Hierarchy, feature_category: :team_planning d
|
|||
|
||||
it { is_expected.to contain_exactly(parent_link1.work_item, parent_link2.work_item) }
|
||||
|
||||
context 'when ordered by relative position and created_at' do
|
||||
let_it_be(:oldest_child) { create(:work_item, :task, project: project, created_at: 5.minutes.ago) }
|
||||
let_it_be(:newest_child) { create(:work_item, :task, project: project, created_at: 5.minutes.from_now) }
|
||||
context 'when ordered by relative position and work_item_id' do
|
||||
let_it_be(:oldest_child) { create(:work_item, :task, project: project) }
|
||||
let_it_be(:newest_child) { create(:work_item, :task, project: project) }
|
||||
|
||||
let_it_be_with_reload(:link_to_oldest_child) do
|
||||
create(:parent_link, work_item_parent: work_item_parent, work_item: oldest_child)
|
||||
|
|
@ -48,10 +48,10 @@ RSpec.describe WorkItems::Widgets::Hierarchy, feature_category: :team_planning d
|
|||
create(:parent_link, work_item_parent: work_item_parent, work_item: newest_child)
|
||||
end
|
||||
|
||||
let(:parent_links_ordered) { [link_to_oldest_child, parent_link1, parent_link2, link_to_newest_child] }
|
||||
let(:parent_links_ordered) { [parent_link1, parent_link2, link_to_oldest_child, link_to_newest_child] }
|
||||
|
||||
context 'when children relative positions are nil' do
|
||||
it 'orders by created_at' do
|
||||
it 'orders by work_item_id' do
|
||||
is_expected.to eq(parent_links_ordered.map(&:work_item))
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ RSpec.describe 'Query.work_item(id)', feature_category: :team_planning do
|
|||
)
|
||||
end
|
||||
|
||||
let_it_be(:child_item1) { create(:work_item, :task, project: project) }
|
||||
let_it_be(:child_item2) { create(:work_item, :task, confidential: true, project: project) }
|
||||
let_it_be(:child_item1) { create(:work_item, :task, project: project, id: 1200) }
|
||||
let_it_be(:child_item2) { create(:work_item, :task, confidential: true, project: project, id: 1400) }
|
||||
let_it_be(:child_link1) { create(:parent_link, work_item_parent: work_item, work_item: child_item1) }
|
||||
let_it_be(:child_link2) { create(:parent_link, work_item_parent: work_item, work_item: child_item2) }
|
||||
|
||||
|
|
@ -258,9 +258,9 @@ RSpec.describe 'Query.work_item(id)', feature_category: :team_planning do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when ordered by default by created_at' do
|
||||
let_it_be(:newest_child) { create(:work_item, :task, project: project, created_at: 5.minutes.from_now) }
|
||||
let_it_be(:oldest_child) { create(:work_item, :task, project: project, created_at: 5.minutes.ago) }
|
||||
context 'when ordered by default by work_item_id' do
|
||||
let_it_be(:newest_child) { create(:work_item, :task, project: project, id: 2000) }
|
||||
let_it_be(:oldest_child) { create(:work_item, :task, project: project, id: 1000) }
|
||||
let_it_be(:newest_link) { create(:parent_link, work_item_parent: work_item, work_item: newest_child) }
|
||||
let_it_be(:oldest_link) { create(:parent_link, work_item_parent: work_item, work_item: oldest_child) }
|
||||
|
||||
|
|
@ -276,7 +276,7 @@ RSpec.describe 'Query.work_item(id)', feature_category: :team_planning do
|
|||
end
|
||||
|
||||
context 'when relative position is set' do
|
||||
let_it_be(:first_child) { create(:work_item, :task, project: project, created_at: 5.minutes.from_now) }
|
||||
let_it_be(:first_child) { create(:work_item, :task, project: project, id: 3000) }
|
||||
|
||||
let_it_be(:first_link) do
|
||||
create(:parent_link, work_item_parent: work_item, work_item: first_child, relative_position: 1)
|
||||
|
|
|
|||
|
|
@ -127,8 +127,8 @@ describe Sidekiq::Metrics do
|
|||
assert_equal 2, job_result.series.dig("p", "22:03")
|
||||
assert_equal 3, job_result.totals["p"]
|
||||
# Execution time is not consistent, so these assertions are not exact
|
||||
assert job_result.total_avg("ms").between?(0.5, 2), job_result.total_avg("ms")
|
||||
assert job_result.series_avg("s")["22:03"].between?(0.0005, 0.002), job_result.series_avg("s")
|
||||
assert job_result.total_avg("ms").between?(0.5, 8), job_result.total_avg("ms")
|
||||
assert job_result.series_avg("s")["22:03"].between?(0.0005, 0.008), job_result.series_avg("s")
|
||||
end
|
||||
|
||||
it "fetches job-specific data" do
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ require (
|
|||
github.com/jpillora/backoff v1.0.0
|
||||
github.com/mitchellh/copystructure v1.2.0
|
||||
github.com/prometheus/client_golang v1.19.1-0.20240328134234-93cf5d4f5f78
|
||||
github.com/redis/go-redis/v9 v9.5.2
|
||||
github.com/redis/go-redis/v9 v9.5.3
|
||||
github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/smartystreets/goconvey v1.8.1
|
||||
|
|
|
|||
|
|
@ -418,8 +418,8 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k
|
|||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/prometheus/prometheus v0.50.1 h1:N2L+DYrxqPh4WZStU+o1p/gQlBaqFbcLBTjlp3vpdXw=
|
||||
github.com/prometheus/prometheus v0.50.1/go.mod h1:FvE8dtQ1Ww63IlyKBn1V4s+zMwF9kHkVNkQBR1pM4CU=
|
||||
github.com/redis/go-redis/v9 v9.5.2 h1:L0L3fcSNReTRGyZ6AqAEN0K56wYeYAwapBIhkvh0f3E=
|
||||
github.com/redis/go-redis/v9 v9.5.2/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/redis/go-redis/v9 v9.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU=
|
||||
github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
|
|
|
|||
Loading…
Reference in New Issue