Skip to content

Group-level Protected Environment [RUN ALL RSPEC] [RUN AS-IF-FOSS]

Shinya Maeda requested to merge group-level-protected-environment-alpha into master

NOTE: This is a blocker against the critical issue for important prospects, thus the priority is relatively high

What does this MR do?

This MR adds the feature - Group-level Protected Environments. Specifically, it changes the following things:

  • Modify protected_environments table to have group_id FK relationship, which points to a group where the Protected Environment configuration resides.
  • Previously, an environment (A row of environments table) can only have one protection, because environments and protection are unique on the project_id and name. In the new architecture, an environment can have multiple protections that inherited from ancestor groups.
  • At group-level configuration, the environment tier name is used for matching instead of its bare name. This is a flexible feature design that can cover variety of production names. You can see the issue about more of the context of this feature design.
  • There are three types of access-level authorization, role-based, user-based and group-based. These are working in the same way at the group-level too.
    • The registrable user/group to deploy access levels are slightly different due to the conceptual differences between project and group:
      • user-based:
        • Project-level config: The given user must be a project member with Developer role or above.
        • Group-level config: The given user must be group member with Maintainer role or above.
      • group-based:
        • Project-level config: The given group must be invited in the project.
        • Group-level config: The given group must be under the group.
  • Add Group-level v4 Rest API. This is straightforward that mirrored from project-level API. We'd follow-up to refactor for making it DRY at next.
  • This feature is behind group_level_protected_environments feature flag, which is disabled by default. This feature will be enabled for a specific prospect only and would NOT be released in this milestone without their confirmation. See timeline of this.

If you're interested, see the issue about the product concept/design.

This MR is built on top of Follow up refactoring for group-level protected environments.

Manual QA

Here are the various test results that performed on a local development instance.

Authorization test

Preparation

  1. Create a group
    1. Create a development project under the group
    2. Create a sub group as an operator group
  2. Administrator of the group creates a group-level protected environment with the following access level settings:
    1. Allow the operator group to deploy to production tier environments

Expectation

  • User-A who is a part of operator group
    • can deploy to production tier environments.
    • can NOT deploy to other environments.
  • User-B who is a maintainer of the development project
    • can NOT deploy to production tier environments.
    • can deploy to other environments.
Date: Thu 20 May 2021 06:45:23 AM UTC
Result: PASSED

API tests

Get the list of group-level protected environments

shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl -H 'Private-token: vQBNNAPV_6UHdn3VJk3e' http://local.gitlab.test:8181/api/v4/groups/202/protected_environments
[{"name":"production","deploy_access_levels":[{"access_level":40,"access_level_description":"Maintainers","user_id":null,"group_id":null}]}]

Get the list of group-level protected environments

shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl -H 'Private-token: vQBNNAPV_6UHdn3VJk3e' http://local.gitlab.test:8181/api/v4/groups/202/protected_environments/production
{"name":"production","deploy_access_levels":[{"access_level":40,"access_level_description":"Maintainers","user_id":null,"group_id":null}]}

Protect environments under a group

Role-base authorization

shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl -H 'Private-token: vQBNNAPV_6UHdn3VJk3e' -H 'Content-type: application/json' -d '{"name": "production", "deploy_access_levels": [{"access_level": 40}]}' http://local.gitlab.test:8181/api/v4/groups/202/protected_environments
{"name":"production","deploy_access_levels":[{"access_level":40,"access_level_description":"Maintainers","user_id":null,"group_id":null}]}

User-base authorization

shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl -H 'Private-token: vQBNNAPV_6UHdn3VJk3e' -H 'Content-type: application/json' -d '{"name": "production", "deploy_access_levels": [{"user_id": 1}]}' http://local.gitlab.test:8181/api/v4/groups/202/protected_environments
{"name":"production","deploy_access_levels":[{"access_level":40,"access_level_description":"Administrator","user_id":1,"group_id":null}]}

Group-base authorization

shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl -H 'Private-token: vQBNNAPV_6UHdn3VJk3e' -H 'Content-type: application/json' -d '{"name": "production", "deploy_access_levels": [{"group_id": 203}]}' http://local.gitlab.test:8181/api/v4/groups/202/protected_environments
{"name":"production","deploy_access_levels":[{"access_level":40,"access_level_description":"a-group","user_id":null,"group_id":203}]}

Unprotect environments under a group

shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl -H 'Private-token: vQBNNAPV_6UHdn3VJk3e' -X DELETE http://local.gitlab.test:8181/api/v4/groups/202/protected_environments/production

Database Migration

shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ tre bin/rails db:migrate:redo VERSION=20210520102039
INFO: This script is a predefined script in devkitkat.
== 20210520102039 GroupProtectedEnvironmentsAddColumn: reverting ==============
-- change_column_null(:protected_environments, :project_id, false)
   -> 0.0013s
-- remove_column(:protected_environments, :group_id)
   -> 0.0007s
== 20210520102039 GroupProtectedEnvironmentsAddColumn: reverted (0.0020s) =====

== 20210520102039 GroupProtectedEnvironmentsAddColumn: migrating ==============
-- add_column(:protected_environments, :group_id, :bigint)
   -> 0.0012s
-- change_column_null(:protected_environments, :project_id, true)
   -> 0.0005s
== 20210520102039 GroupProtectedEnvironmentsAddColumn: migrated (0.0017s) =====
shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ tre bin/rails db:migrate:redo VERSION=20210601090039
INFO: This script is a predefined script in devkitkat.
== 20210601090039 GroupProtectedEnvironmentsAddIndexAndConstraint: reverting ==
-- execute("      DELETE FROM protected_environments WHERE group_id IS NOT NULL\n")
   -> 0.0012s
-- execute("ALTER TABLE protected_environments\nDROP CONSTRAINT IF EXISTS protected_environments_project_or_group_existence\n")
   -> 0.0011s
-- foreign_keys(:protected_environments)
   -> 0.0019s
-- remove_foreign_key(:protected_environments, {:column=>:group_id})
   -> 0.0036s
-- transaction_open?()
   -> 0.0000s
-- indexes(:protected_environments)
   -> 0.0017s
-- remove_index(:protected_environments, {:algorithm=>:concurrently, :name=>"index_protected_environments_on_group_id_and_name"})
   -> 0.0013s
== 20210601090039 GroupProtectedEnvironmentsAddIndexAndConstraint: reverted (0.0175s) 

== 20210601090039 GroupProtectedEnvironmentsAddIndexAndConstraint: migrating ==
-- transaction_open?()
   -> 0.0000s
-- index_exists?(:protected_environments, [:group_id, :name], {:unique=>true, :name=>"index_protected_environments_on_group_id_and_name", :where=>"group_id IS NOT NULL", :algorithm=>:concurrently})
   -> 0.0015s
-- add_index(:protected_environments, [:group_id, :name], {:unique=>true, :name=>"index_protected_environments_on_group_id_and_name", :where=>"group_id IS NOT NULL", :algorithm=>:concurrently})
   -> 0.0040s
-- transaction_open?()
   -> 0.0000s
-- foreign_keys(:protected_environments)
   -> 0.0017s
-- execute("ALTER TABLE protected_environments\nADD CONSTRAINT fk_9e112565b7\nFOREIGN KEY (group_id)\nREFERENCES namespaces (id)\nON DELETE CASCADE\nNOT VALID;\n")
   -> 0.0009s
-- execute("ALTER TABLE protected_environments VALIDATE CONSTRAINT fk_9e112565b7;")
   -> 0.0014s
-- transaction_open?()
   -> 0.0000s
-- current_schema()
   -> 0.0001s
-- execute("ALTER TABLE protected_environments\nADD CONSTRAINT protected_environments_project_or_group_existence\nCHECK ( ((project_id IS NULL) != (group_id IS NULL)) )\nNOT VALID;\n")
   -> 0.0005s
-- current_schema()
   -> 0.0001s
-- execute("ALTER TABLE protected_environments VALIDATE CONSTRAINT protected_environments_project_or_group_existence;")
   -> 0.0008s
== 20210601090039 GroupProtectedEnvironmentsAddIndexAndConstraint: migrated (0.0173s) 

Does this MR meet the acceptance criteria?

Conformity

Availability and Testing

Edited by Shinya Maeda

Merge request reports