Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
433ee53e3e
commit
aca89cb7e9
|
|
@ -1 +1 @@
|
|||
a6091637dcb4c3b601a8860b5f164c0ce90ba0ca
|
||||
d02d473234dc18649e5a359ee203d6a3aa5c4031
|
||||
|
|
|
|||
|
|
@ -328,6 +328,7 @@ class ProjectPolicy < BasePolicy
|
|||
enable :move_design
|
||||
enable :destroy_design
|
||||
enable :read_terraform_state
|
||||
enable :read_pod_logs
|
||||
end
|
||||
|
||||
rule { can?(:developer_access) & user_confirmed? }.policy do
|
||||
|
|
@ -367,7 +368,6 @@ class ProjectPolicy < BasePolicy
|
|||
enable :admin_operations
|
||||
enable :read_deploy_token
|
||||
enable :create_deploy_token
|
||||
enable :read_pod_logs
|
||||
enable :destroy_deploy_token
|
||||
enable :read_prometheus_alerts
|
||||
enable :admin_terraform_state
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
= render 'groups/settings/default_branch_protection', f: f, group: @group
|
||||
= render 'groups/settings/project_creation_level', f: f, group: @group
|
||||
= render 'groups/settings/subgroup_creation_level', f: f, group: @group
|
||||
= render_if_exists 'groups/settings/prevent_forking', f: f, group: @group
|
||||
= render 'groups/settings/two_factor_auth', f: f
|
||||
= render_if_exists 'groups/personal_access_token_expiration_policy', f: f, group: @group
|
||||
= render_if_exists 'groups/member_lock_setting', f: f, group: @group
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ module ApplicationWorker
|
|||
include Sidekiq::Worker # rubocop:disable Cop/IncludeSidekiqWorker
|
||||
include WorkerAttributes
|
||||
include WorkerContext
|
||||
include Gitlab::SidekiqVersioning::Worker
|
||||
|
||||
LOGGING_EXTRA_KEY = 'extra'
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add Prevent forking outside group feature
|
||||
merge_request: 36848
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: "Add NuGet lock files support to Dependency Scanning CI template"
|
||||
merge_request: 37553
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Allow users with developer access level for given project to view kubernetes
|
||||
pod logs
|
||||
merge_request: 38467
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
name: sidekiq_versioning
|
||||
introduced_by_url:
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/232934
|
||||
group: group::fulfillment
|
||||
type: development
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddPreventForkingToNamespaceSettings < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
add_column :namespace_settings, :prevent_forking_outside_group, :boolean, null: false, default: false
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
dbb84d05cfe6d2ef143b9321b2b089c66d705f01ced64756032622f64f8e3eed
|
||||
|
|
@ -13257,7 +13257,8 @@ CREATE TABLE public.namespace_root_storage_statistics (
|
|||
CREATE TABLE public.namespace_settings (
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
updated_at timestamp with time zone NOT NULL,
|
||||
namespace_id integer NOT NULL
|
||||
namespace_id integer NOT NULL,
|
||||
prevent_forking_outside_group boolean DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE public.namespace_statistics (
|
||||
|
|
|
|||
|
|
@ -750,6 +750,7 @@ PUT /groups/:id
|
|||
| `file_template_project_id` | integer | no | **(PREMIUM)** The ID of a project to load custom file templates from. |
|
||||
| `shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Pipeline minutes quota for this group (included in plan). Can be `nil` (default; inherit system default), `0` (unlimited) or `> 0` |
|
||||
| `extra_shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Extra pipeline minutes quota for this group (purchased in addition to the minutes included in the plan). |
|
||||
| `prevent_forking_outside_group` | boolean | no | **(PREMIUM)** When enabled, users can **not** fork projects from this group to external namespaces
|
||||
|
||||
NOTE: **Note:**
|
||||
The `projects` and `shared_projects` attributes in the response are deprecated and will be [removed in API v5](https://gitlab.com/gitlab-org/gitlab/-/issues/213797).
|
||||
|
|
|
|||
|
|
@ -219,10 +219,30 @@ For example, to add support for files referenced by a `Widget` model with a
|
|||
def carrierwave_uploader
|
||||
model_record.file
|
||||
end
|
||||
|
||||
# Change this to `true` to release replication of this model. Then remove
|
||||
# this override in the next release.
|
||||
# The feature flag follows the format `geo_#{replicable_name}_replication`,
|
||||
# so here it would be `geo_widget_replication`
|
||||
def self.replication_enabled_by_default?
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
1. Add this replicator class to the method `replicator_classes` in
|
||||
`ee/lib/gitlab/geo.rb`:
|
||||
|
||||
```ruby
|
||||
def self.replicator_classes
|
||||
classes = [::Geo::PackageFileReplicator,
|
||||
::Geo::WidgetReplicator]
|
||||
|
||||
classes.select(&:enabled?)
|
||||
end
|
||||
```
|
||||
|
||||
1. Create `ee/spec/replicators/geo/widget_replicator_spec.rb` and perform
|
||||
the setup necessary to define the `model_record` variable for the shared
|
||||
examples.
|
||||
|
|
@ -239,13 +259,15 @@ For example, to add support for files referenced by a `Widget` model with a
|
|||
end
|
||||
```
|
||||
|
||||
1. Create the `widget_registry` table so Geo secondaries can track the sync and
|
||||
1. Create the `widget_registry` table, with columns ordered according to [our guidelines](../ordering_table_columns.md) so Geo secondaries can track the sync and
|
||||
verification state of each Widget's file. This migration belongs in `ee/db/geo/migrate`:
|
||||
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
class CreateWidgetRegistry < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
|
@ -257,12 +279,12 @@ For example, to add support for files referenced by a `Widget` model with a
|
|||
t.integer :widget_id, null: false
|
||||
t.integer :state, default: 0, null: false, limit: 2
|
||||
t.integer :retry_count, default: 0, limit: 2
|
||||
t.text :last_sync_failure
|
||||
t.datetime_with_timezone :retry_at
|
||||
t.datetime_with_timezone :last_synced_at
|
||||
t.datetime_with_timezone :created_at, null: false
|
||||
t.text :last_sync_failure
|
||||
|
||||
t.index :widget_id
|
||||
t.index :widget_id, name: :index_widget_registry_on_widget_id
|
||||
t.index :retry_at
|
||||
t.index :state
|
||||
end
|
||||
|
|
@ -358,8 +380,13 @@ Widgets should now be replicated by Geo!
|
|||
|
||||
#### Verification
|
||||
|
||||
1. Add verification state fields to the `widgets` table so the Geo primary can
|
||||
track verification state:
|
||||
There are two ways to add verification related fields so that the Geo primary
|
||||
can track verification state:
|
||||
|
||||
##### Option 1: Add verification state fields to the existing `widgets` table itself
|
||||
|
||||
1. Add a migration to add columns ordered according to [our guidelines](../ordering_table_columns.md)
|
||||
for verification state to the widgets table:
|
||||
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
|
@ -382,7 +409,7 @@ Widgets should now be replicated by Geo!
|
|||
end
|
||||
```
|
||||
|
||||
Adding a `text` column also [requires](../database/strings_and_the_text_data_type.md#add-a-text-column-to-an-existing-table)
|
||||
1. Adding a `text` column also [requires](../database/strings_and_the_text_data_type.md#add-a-text-column-to-an-existing-table)
|
||||
setting a limit:
|
||||
|
||||
```ruby
|
||||
|
|
@ -400,7 +427,7 @@ Widgets should now be replicated by Geo!
|
|||
```
|
||||
|
||||
1. Add a partial index on `verification_failure` and `verification_checksum` to ensure
|
||||
re-verification can be performed efficiently:
|
||||
re-verification can be performed efficiently. Add a migration in `ee/db/geo/migrate/`:
|
||||
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
|
@ -424,6 +451,65 @@ Widgets should now be replicated by Geo!
|
|||
end
|
||||
```
|
||||
|
||||
##### Option 2: Create a separate `widget_states` table with verification state fields
|
||||
|
||||
1. Add a migration in `ee/db/geo/migrate/` to create a `widget_states` table and add a
|
||||
partial index on `verification_failure` and `verification_checksum` to ensure
|
||||
re-verification can be performed efficiently. Order the columns according to [our guidelines](../ordering_table_columns.md):
|
||||
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
class CreateWidgetStates < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
unless table_exists?(:widget_states)
|
||||
with_lock_retries do
|
||||
create_table :widget_states, id: false do |t|
|
||||
t.references :widget, primary_key: true, null: false, foreign_key: { on_delete: :cascade }
|
||||
t.datetime_with_timezone :verification_retry_at
|
||||
t.datetime_with_timezone :verified_at
|
||||
t.integer :verification_retry_count, limit: 2
|
||||
t.binary :verification_checksum, using: 'verification_checksum::bytea'
|
||||
t.text :verification_failure
|
||||
|
||||
t.index :verification_failure, where: "(verification_failure IS NOT NULL)", name: "widgets_verification_failure_partial"
|
||||
t.index :verification_checksum, where: "(verification_checksum IS NOT NULL)", name: "widgets_verification_checksum_partial"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
add_text_limit :widget_states, :verification_failure, 255
|
||||
end
|
||||
|
||||
def down
|
||||
drop_table :widget_states
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
1. Add the following lines to the `widget` model:
|
||||
|
||||
```ruby
|
||||
class Widget < ApplicationRecord
|
||||
...
|
||||
has_one :widget_state, inverse_of: :widget
|
||||
|
||||
delegate :verification_retry_at, :verification_retry_at=,
|
||||
:verified_at, :verified_at=,
|
||||
:verification_checksum, :verification_checksum=,
|
||||
:verification_failure, :verification_failure=,
|
||||
:verification_retry_count, :verification_retry_count=,
|
||||
to: :widget_state
|
||||
...
|
||||
end
|
||||
```
|
||||
|
||||
To do: Add verification on secondaries. This should be done as part of
|
||||
[Geo: Self Service Framework - First Implementation for Package File verification](https://gitlab.com/groups/gitlab-org/-/epics/1817)
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,36 @@ the extra jobs will take resources away from jobs from workers that were already
|
|||
there, if the resources available to the Sidekiq process handling the namespace
|
||||
are not adjusted appropriately.
|
||||
|
||||
## Versioning
|
||||
|
||||
Version can be specified on each Sidekiq worker class.
|
||||
This is then sent along when the job is created.
|
||||
|
||||
```ruby
|
||||
class FooWorker
|
||||
include ApplicationWorker
|
||||
|
||||
version 2
|
||||
|
||||
def perform(*args)
|
||||
if job_version == 2
|
||||
foo = args.first['foo']
|
||||
else
|
||||
foo = args.first
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Under this schema, any worker is expected to be able to handle any job that was
|
||||
enqueued by an older version of that worker. This means that when changing the
|
||||
arguments a worker takes, you must increment the `version` (or set `version 1`
|
||||
if this is the first time a worker's arguments are changing), but also make sure
|
||||
that the worker is still able to handle jobs that were queued with any earlier
|
||||
version of the arguments. From the worker's `perform` method, you can read
|
||||
`self.job_version` if you want to specifically branch on job version, or you
|
||||
can read the number or type of provided arguments.
|
||||
|
||||
## Idempotent Jobs
|
||||
|
||||
It's known that a job can fail for multiple reasons. For example, network outages or bugs.
|
||||
|
|
|
|||
|
|
@ -668,6 +668,23 @@ To enable delayed deletion of projects:
|
|||
1. Expand the **Permissions, LFS, 2FA** section, and check **Enable delayed project removal**.
|
||||
1. Click **Save changes**.
|
||||
|
||||
#### Prevent project forking outside group **(PREMIUM)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/216987) in GitLab 13.3.
|
||||
|
||||
By default, projects within a group can be forked.
|
||||
Optionally, on [Premium or Silver](https://about.gitlab.com/pricing/) or higher tiers,
|
||||
you can prevent the projects within a group from being forked outside of the current top-level group.
|
||||
|
||||
Previously this setting was available only for groups enforcing group managed account. This setting will be
|
||||
removed from SAML setting page and migrated to group setting, but in the interim period of changes both of those settings will be taken into consideration, if even one is set to `true` then it will be assumed group does not allow forking projects outside.
|
||||
|
||||
To enable prevent project forking:
|
||||
|
||||
1. Navigate to the top-level group's **Settings > General** page.
|
||||
1. Expand the **Permissions, LFS, 2FA** section, and check **Prevent project forking outside current group**.
|
||||
1. Click **Save changes**.
|
||||
|
||||
### Advanced settings
|
||||
|
||||
- **Projects**: View all projects within that group, add members to each project,
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ The following table depicts the various user permission levels in a project.
|
|||
| Remove GitLab Pages | | | | ✓ | ✓ |
|
||||
| Manage clusters | | | | ✓ | ✓ |
|
||||
| Manage Project Operations | | | | ✓ | ✓ |
|
||||
| View Pods logs | | | | ✓ | ✓ |
|
||||
| View Pods logs | | | ✓ | ✓ | ✓ |
|
||||
| Read Terraform state | | | ✓ | ✓ | ✓ |
|
||||
| Manage Terraform state | | | | ✓ | ✓ |
|
||||
| Manage license policy **(ULTIMATE)** | | | | ✓ | ✓ |
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ stage: Create
|
|||
group: Source Code
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||
disqus_identifier: 'https://docs.gitlab.com/ee/workflow/repository_mirroring.html'
|
||||
stage: Create
|
||||
group: Source Code
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||
---
|
||||
|
||||
# Repository mirroring
|
||||
|
|
@ -124,7 +127,87 @@ To set up a mirror from GitLab to GitHub, you need to follow these steps:
|
|||
|
||||
The mirrored repository will be listed. For example, `https://*****:*****@github.com/<your_github_group>/<your_github_project>.git`.
|
||||
|
||||
The repository will push soon. To force a push, click the appropriate button.
|
||||
The repository will push soon. To force a push, click the **Update now** (**{retry}**) button.
|
||||
|
||||
## Setting up a push mirror from GitLab to AWS CodeCommit
|
||||
|
||||
AWS CodeCommit push mirroring is currently the best way to connect GitLab repositories to AWS CodePipeline, as GitLab is not yet supported as one of their Source Code Management (SCM) providers.
|
||||
|
||||
Each new AWS Codepipeline needs significant AWS infrastructure setup. It also requires an individual pipeline per branch.
|
||||
|
||||
If AWS CodeDeploy is the final step of a CodePipeline, you can, instead, leverage GitLab CI/CD pipelines and simply use the AWS CLI in the final job in `.gitlab-ci.yml` to deploy to CodeDeploy.
|
||||
|
||||
NOTE: **Note:**
|
||||
GitLab-to-AWS-CodeCommit push mirroring cannot use SSH authentication until [GitLab issue 34014](https://gitlab.com/gitlab-org/gitlab/-/issues/34014) is resolved.
|
||||
|
||||
To set up a mirror from GitLab to AWS CodeCommit:
|
||||
|
||||
1. In the AWS IAM console, create an IAM user.
|
||||
1. Add the following least privileges permissions for repository mirroring as an "inline policy".
|
||||
|
||||
The Amazon Resource Names (ARNs) must explicitly include the region and account. The IAM policy
|
||||
below grants privilege for mirroring access to two sample repositories. These permissions have
|
||||
been tested to be the minimum (least privileged) required for mirroring:
|
||||
|
||||
```json
|
||||
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "MinimumGitLabPushMirroringPermissions",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"codecommit:GitPull",
|
||||
"codecommit:GitPush"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:codecommit:us-east-1:111111111111:MyDestinationRepo",
|
||||
"arn:aws:codecommit:us-east-1:111111111111:MyDemo*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
```json
|
||||
|
||||
1. After the user was created, click the AWS IAM user name.
|
||||
1. Click the **Security credentials** tab.
|
||||
1. Under **HTTPS Git credentials for AWS CodeCommit** click **Generate credentials**.
|
||||
|
||||
NOTE: **Note:**
|
||||
This Git user ID and password is specific to communicating with CodeCommit. Do
|
||||
not confuse it with the IAM user ID or AWS keys of this user.
|
||||
|
||||
1. Copy or download special Git HTTPS user ID and password.
|
||||
1. In the AWS CodeCommit console, create a new repository to mirror from your GitLab repo.
|
||||
1. Open your new repository and click **Clone URL > Clone HTTPS** (not **Clone HTTPS (GRC)**).
|
||||
1. In GitLab, open the repository to be push-mirrored.
|
||||
1. Click **Settings > Repository** and expand **Mirroring repositories**.
|
||||
1. Fill in the **Git repository URL** field using this format:
|
||||
|
||||
```plaintext
|
||||
https://<your_aws_git_userid>@git-codecommit.<aws-region>.amazonaws.com/v1/repos/<your_codecommit_repo>
|
||||
```
|
||||
|
||||
Replace `<your_aws_git_userid>` with the AWS **special HTTPS Git user ID** from the IAM Git
|
||||
credentials created earlier. Replace `<your_codecommit_repo>` with the name of your repo in CodeCommit.
|
||||
|
||||
1. For **Mirror direction**, select **Push**.
|
||||
1. For **Authentication method**, select **Password** and fill in the **Password** field with the special IAM Git clone user ID **password** created earlier in AWS.
|
||||
1. The option **Only mirror protected branches** should be good for CodeCommit as it pushes more
|
||||
frequently (from every five minutes to every minute).
|
||||
CodePipeline requires individual pipeline setups for named branches you wish to have a AWS CI setup for. Since feature branches that have dynamic names will not be supported anyway, configuring **Only mirror protected branches** does not cause flexibility problems with CodePipeline integration as long as you are also willing to protect all the named branches you want to build CodePipelines for.
|
||||
|
||||
1. Click **Mirror repository**. You should see the mirrored repository appear:
|
||||
|
||||
```plaintext
|
||||
https://*****:*****@git-codecommit.<aws-region>.amazonaws.com/v1/repos/<your_codecommit_repo>
|
||||
```
|
||||
|
||||
To test mirroring by forcing a push, click the half-circle arrows button (hover text is **Update now**).
|
||||
If **Last successful update** shows a date, you have configured mirroring correctly.
|
||||
If it is not working correctly a red `error` tag appears and shows the error message as hover text.
|
||||
|
||||
## Setting up a push mirror to another GitLab instance with 2FA activated
|
||||
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ gemnasium-dependency_scanning:
|
|||
- '{npm-shrinkwrap.json,*/npm-shrinkwrap.json,*/*/npm-shrinkwrap.json}'
|
||||
- '{package-lock.json,*/package-lock.json,*/*/package-lock.json}'
|
||||
- '{yarn.lock,*/yarn.lock,*/*/yarn.lock}'
|
||||
- '{packages.lock.json,*/packages.lock.json,*/*/packages.lock.json}'
|
||||
|
||||
gemnasium-maven-dependency_scanning:
|
||||
extends: .ds-analyzer
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def graphql_query?
|
||||
request.post? && request.path.start_with?(GRAPHQL_URL)
|
||||
request.post? && request.path.start_with?(File.join(relative_url, GRAPHQL_URL))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ module Gitlab
|
|||
keys = keys.map { |key| cache_key(key) }
|
||||
|
||||
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
|
||||
unlink_or_delete(redis, keys)
|
||||
redis.unlink(*keys)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -60,17 +60,5 @@ module Gitlab
|
|||
def with(&blk)
|
||||
Gitlab::Redis::Cache.with(&blk) # rubocop:disable CodeReuse/ActiveRecord
|
||||
end
|
||||
|
||||
def unlink_or_delete(redis, keys)
|
||||
if Feature.enabled?(:repository_set_cache_unlink, default_enabled: true)
|
||||
redis.unlink(*keys)
|
||||
else
|
||||
redis.del(*keys)
|
||||
end
|
||||
rescue ::Redis::CommandError => e
|
||||
Gitlab::ErrorTracking.log_exception(e)
|
||||
|
||||
redis.del(*keys)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ module Gitlab
|
|||
chain.add ::Labkit::Middleware::Sidekiq::Server
|
||||
chain.add ::Gitlab::SidekiqMiddleware::InstrumentationLogger
|
||||
chain.add ::Gitlab::SidekiqMiddleware::AdminMode::Server
|
||||
chain.add ::Gitlab::SidekiqVersioning::Middleware
|
||||
chain.add ::Gitlab::SidekiqStatus::ServerMiddleware
|
||||
chain.add ::Gitlab::SidekiqMiddleware::WorkerContext::Server
|
||||
chain.add ::Gitlab::SidekiqMiddleware::DuplicateJobs::Server
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module SidekiqVersioning
|
||||
class Middleware
|
||||
def call(worker, job, queue)
|
||||
worker.job_version = job['version'] if worker.is_a?(ApplicationWorker) && Feature.enabled?(:sidekiq_versioning)
|
||||
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module SidekiqVersioning
|
||||
module Worker
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
version 0
|
||||
|
||||
attr_writer :job_version
|
||||
end
|
||||
|
||||
class_methods do
|
||||
def version(new_version = nil)
|
||||
if new_version
|
||||
sidekiq_options version: new_version.to_i
|
||||
else
|
||||
get_sidekiq_options['version']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Version is not set if `new.perform` is called directly,
|
||||
# and in that case we fallback to latest version
|
||||
def job_version
|
||||
@job_version ||= self.class.version
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -51,7 +51,7 @@ module Gitlab
|
|||
|
||||
def topology_app_requests_per_hour(client)
|
||||
result = query_safely('gitlab_usage_ping:ops:rate5m', 'app_requests', fallback: nil) do |query|
|
||||
client.query(one_week_average(query)).first
|
||||
client.query(aggregate_one_week(query)).first
|
||||
end
|
||||
|
||||
return unless result
|
||||
|
|
@ -84,14 +84,14 @@ module Gitlab
|
|||
end
|
||||
|
||||
def topology_node_memory(client)
|
||||
query_safely('gitlab_usage_ping:node_memory_total_bytes:avg', 'node_memory', fallback: {}) do |query|
|
||||
aggregate_by_instance(client, one_week_average(query))
|
||||
query_safely('gitlab_usage_ping:node_memory_total_bytes:max', 'node_memory', fallback: {}) do |query|
|
||||
aggregate_by_instance(client, aggregate_one_week(query, aggregation: :max))
|
||||
end
|
||||
end
|
||||
|
||||
def topology_node_cpus(client)
|
||||
query_safely('gitlab_usage_ping:node_cpus:count', 'node_cpus', fallback: {}) do |query|
|
||||
aggregate_by_instance(client, one_week_average(query, aggregation: :max))
|
||||
aggregate_by_instance(client, aggregate_one_week(query, aggregation: :max))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -114,25 +114,25 @@ module Gitlab
|
|||
def topology_service_memory_rss(client)
|
||||
query_safely(
|
||||
'gitlab_usage_ping:node_service_process_resident_memory_bytes:avg', 'service_rss', fallback: {}
|
||||
) { |query| aggregate_by_labels(client, one_week_average(query)) }
|
||||
) { |query| aggregate_by_labels(client, aggregate_one_week(query)) }
|
||||
end
|
||||
|
||||
def topology_service_memory_uss(client)
|
||||
query_safely(
|
||||
'gitlab_usage_ping:node_service_process_unique_memory_bytes:avg', 'service_uss', fallback: {}
|
||||
) { |query| aggregate_by_labels(client, one_week_average(query)) }
|
||||
) { |query| aggregate_by_labels(client, aggregate_one_week(query)) }
|
||||
end
|
||||
|
||||
def topology_service_memory_pss(client)
|
||||
query_safely(
|
||||
'gitlab_usage_ping:node_service_process_proportional_memory_bytes:avg', 'service_pss', fallback: {}
|
||||
) { |query| aggregate_by_labels(client, one_week_average(query)) }
|
||||
) { |query| aggregate_by_labels(client, aggregate_one_week(query)) }
|
||||
end
|
||||
|
||||
def topology_all_service_process_count(client)
|
||||
query_safely(
|
||||
'gitlab_usage_ping:node_service_process:count', 'service_process_count', fallback: {}
|
||||
) { |query| aggregate_by_labels(client, one_week_average(query)) }
|
||||
) { |query| aggregate_by_labels(client, aggregate_one_week(query)) }
|
||||
end
|
||||
|
||||
def topology_all_service_server_types(client)
|
||||
|
|
@ -231,7 +231,7 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def one_week_average(query, aggregation: :avg)
|
||||
def aggregate_one_week(query, aggregation: :avg)
|
||||
"#{aggregation}_over_time (#{query}[1w])"
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -10336,6 +10336,9 @@ msgstr ""
|
|||
msgid "FeatureFlags|New feature flag"
|
||||
msgstr ""
|
||||
|
||||
msgid "FeatureFlags|New list"
|
||||
msgstr ""
|
||||
|
||||
msgid "FeatureFlags|Percent rollout (logged in users)"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -12019,6 +12022,12 @@ msgstr ""
|
|||
msgid "GroupSettings|Please choose a group URL with no special characters."
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupSettings|Prevent forking outside of the group"
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupSettings|Prevent forking setting was not saved"
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -12055,6 +12064,9 @@ msgstr ""
|
|||
msgid "GroupSettings|This setting will prevent group members from being notified if the group is mentioned."
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupSettings|This setting will prevent group members from forking projects outside of the group."
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupSettings|Transfer group"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -17935,6 +17947,9 @@ msgstr ""
|
|||
msgid "Prevent environment from auto-stopping"
|
||||
msgstr ""
|
||||
|
||||
msgid "Prevent project forking outside current group"
|
||||
msgstr ""
|
||||
|
||||
msgid "Prevent users from changing their profile name"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -26211,9 +26226,6 @@ msgstr ""
|
|||
msgid "User List"
|
||||
msgstr ""
|
||||
|
||||
msgid "User Lists can only be created and modified with %{linkStart}the API%{linkEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "User OAuth applications"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -26283,6 +26295,9 @@ msgstr ""
|
|||
msgid "UserLists|Define a set of users to be used within feature flag strategies"
|
||||
msgstr ""
|
||||
|
||||
msgid "UserLists|Edit"
|
||||
msgstr ""
|
||||
|
||||
msgid "UserLists|Edit %{name}"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@
|
|||
"@babel/plugin-transform-modules-commonjs": "^7.10.1",
|
||||
"@gitlab/eslint-plugin": "3.1.0",
|
||||
"@testing-library/dom": "^7.16.2",
|
||||
"@vue/test-utils": "^1.0.0-beta.30",
|
||||
"@vue/test-utils": "1.0.0-beta.30",
|
||||
"acorn": "^6.3.0",
|
||||
"axios-mock-adapter": "^1.15.0",
|
||||
"babel-jest": "^24.1.0",
|
||||
|
|
@ -211,7 +211,7 @@
|
|||
"stylelint-config-recommended": "^2.2.0",
|
||||
"stylelint-scss": "^3.9.2",
|
||||
"timezone-mock": "^1.0.8",
|
||||
"vue-jest": "^4.0.0-beta.2",
|
||||
"vue-jest": "4.0.0-beta.2",
|
||||
"webpack-dev-server": "^3.10.3",
|
||||
"xhr-mock": "^2.5.1",
|
||||
"yarn-check-webpack-plugin": "^1.2.0",
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ RSpec.describe Projects::LogsController do
|
|||
describe 'GET #index' do
|
||||
let(:empty_project) { create(:project) }
|
||||
|
||||
it 'returns 404 with developer access' do
|
||||
project.add_developer(user)
|
||||
it 'returns 404 with reporter access' do
|
||||
project.add_reporter(user)
|
||||
|
||||
get :index, params: environment_params
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ RSpec.describe Projects::LogsController do
|
|||
end
|
||||
|
||||
it 'renders empty logs page if no environment exists' do
|
||||
empty_project.add_maintainer(user)
|
||||
empty_project.add_developer(user)
|
||||
|
||||
get :index, params: { namespace_id: empty_project.namespace, project_id: empty_project }
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ RSpec.describe Projects::LogsController do
|
|||
end
|
||||
|
||||
it 'renders index template' do
|
||||
project.add_maintainer(user)
|
||||
project.add_developer(user)
|
||||
|
||||
get :index, params: environment_params
|
||||
|
||||
|
|
@ -69,14 +69,27 @@ RSpec.describe Projects::LogsController do
|
|||
end
|
||||
end
|
||||
|
||||
it 'returns 404 with developer access' do
|
||||
project.add_developer(user)
|
||||
it 'returns 404 with reporter access' do
|
||||
project.add_reporter(user)
|
||||
|
||||
get endpoint, params: environment_params(pod_name: pod_name, format: :json)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
|
||||
context 'with developer access' do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
it 'returns the service result' do
|
||||
get endpoint, params: environment_params(pod_name: pod_name, format: :json)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:success)
|
||||
expect(json_response).to eq(service_result_json)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with maintainer access' do
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :namespace_settings, class: 'NamespaceSetting' do
|
||||
namespace
|
||||
end
|
||||
end
|
||||
|
|
@ -30,6 +30,10 @@ FactoryBot.define do
|
|||
association :root_storage_statistics, factory: :namespace_root_storage_statistics
|
||||
end
|
||||
|
||||
trait :with_namespace_settings do
|
||||
association :namespace_settings, factory: :namespace_settings
|
||||
end
|
||||
|
||||
# Construct a hierarchy underneath the namespace.
|
||||
# Each namespace will have `children` amount of children,
|
||||
# and `depth` levels of descendants.
|
||||
|
|
|
|||
|
|
@ -110,6 +110,19 @@ RSpec.describe Gitlab::Middleware::ReadOnly do
|
|||
expect(subject).not_to disallow_request
|
||||
end
|
||||
|
||||
context 'relative URL is configured' do
|
||||
before do
|
||||
stub_config_setting(relative_url_root: '/gitlab')
|
||||
end
|
||||
|
||||
it 'expects a graphql request to be allowed' do
|
||||
response = request.post("/gitlab/api/graphql")
|
||||
|
||||
expect(response).not_to be_redirect
|
||||
expect(subject).not_to disallow_request
|
||||
end
|
||||
end
|
||||
|
||||
context 'sidekiq admin requests' do
|
||||
where(:mounted_at) do
|
||||
[
|
||||
|
|
|
|||
|
|
@ -93,23 +93,6 @@ RSpec.describe Gitlab::RepositorySetCache, :clean_gitlab_redis_cache do
|
|||
|
||||
it { is_expected.to eq(0) }
|
||||
end
|
||||
|
||||
context "unlink isn't supported" do
|
||||
before do
|
||||
allow_any_instance_of(Redis).to receive(:unlink) { raise ::Redis::CommandError }
|
||||
end
|
||||
|
||||
it 'still deletes the given key' do
|
||||
expect(cache.expire(:foo)).to eq(1)
|
||||
expect(cache.read(:foo)).to be_empty
|
||||
end
|
||||
|
||||
it 'logs the failure' do
|
||||
expect(Gitlab::ErrorTracking).to receive(:log_exception)
|
||||
|
||||
cache.expire(:foo)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#exist?' do
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ RSpec.describe Gitlab::SidekiqMiddleware do
|
|||
Gitlab::SidekiqMiddleware::BatchLoader,
|
||||
Labkit::Middleware::Sidekiq::Server,
|
||||
Gitlab::SidekiqMiddleware::InstrumentationLogger,
|
||||
Gitlab::SidekiqVersioning::Middleware,
|
||||
Gitlab::SidekiqStatus::ServerMiddleware,
|
||||
Gitlab::SidekiqMiddleware::ServerMetrics,
|
||||
Gitlab::SidekiqMiddleware::ArgumentsLogger,
|
||||
|
|
@ -78,6 +79,41 @@ RSpec.describe Gitlab::SidekiqMiddleware do
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples "a server middleware chain for mailer" do
|
||||
let(:worker_class) { ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper }
|
||||
let(:job_args) do
|
||||
[
|
||||
{
|
||||
"job_class" => "ActionMailer::MailDeliveryJob",
|
||||
"job_id" => "a180b47c-3fd6-41b8-81e9-34da61c3400e",
|
||||
"provider_job_id" => nil,
|
||||
"queue_name" => "mailers",
|
||||
"priority" => nil,
|
||||
"arguments" => [
|
||||
"Notify",
|
||||
"test_email",
|
||||
"deliver_now",
|
||||
{
|
||||
"args" => [
|
||||
"test@example.com",
|
||||
"subject",
|
||||
"body"
|
||||
],
|
||||
"_aj_symbol_keys" => ["args"]
|
||||
}
|
||||
],
|
||||
"executions" => 0,
|
||||
"exception_executions" => {},
|
||||
"locale" => "en",
|
||||
"timezone" => "UTC",
|
||||
"enqueued_at" => "2020-07-27T07:43:31Z"
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
it_behaves_like "a server middleware chain"
|
||||
end
|
||||
|
||||
context "all optional middlewares off" do
|
||||
let(:metrics) { false }
|
||||
let(:arguments_logger) { false }
|
||||
|
|
@ -91,6 +127,7 @@ RSpec.describe Gitlab::SidekiqMiddleware do
|
|||
end
|
||||
|
||||
it_behaves_like "a server middleware chain"
|
||||
it_behaves_like "a server middleware chain for mailer"
|
||||
end
|
||||
|
||||
context "all optional middlewares on" do
|
||||
|
|
@ -100,6 +137,7 @@ RSpec.describe Gitlab::SidekiqMiddleware do
|
|||
let(:disabled_sidekiq_middlewares) { [] }
|
||||
|
||||
it_behaves_like "a server middleware chain"
|
||||
it_behaves_like "a server middleware chain for mailer"
|
||||
|
||||
context "server metrics" do
|
||||
let(:gitaly_histogram) { double(:gitaly_histogram) }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::SidekiqVersioning::Middleware do
|
||||
let(:worker_class) do
|
||||
Class.new do
|
||||
def self.name
|
||||
'DummyWorker'
|
||||
end
|
||||
|
||||
include ApplicationWorker
|
||||
|
||||
version 2
|
||||
end
|
||||
end
|
||||
|
||||
describe '#call' do
|
||||
let(:worker) { worker_class.new }
|
||||
let(:job) { { 'version' => 3, 'queue' => queue } }
|
||||
let(:queue) { worker_class.queue }
|
||||
|
||||
def call!(&block)
|
||||
block ||= -> {}
|
||||
subject.call(worker, job, queue, &block)
|
||||
end
|
||||
|
||||
it 'sets worker.job_version' do
|
||||
call!
|
||||
|
||||
expect(worker.job_version).to eq(job['version'])
|
||||
end
|
||||
|
||||
it 'yields' do
|
||||
expect { |b| call!(&b) }.to yield_control
|
||||
end
|
||||
|
||||
context 'when worker is not ApplicationWorker' do
|
||||
let(:worker_class) do
|
||||
ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper
|
||||
end
|
||||
|
||||
it 'does not err' do
|
||||
expect { call! }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sidekiq_versioning is disabled' do
|
||||
before do
|
||||
stub_feature_flags(sidekiq_versioning: false)
|
||||
end
|
||||
|
||||
it 'does not set job_version' do
|
||||
expect(worker).not_to receive(:job_version=)
|
||||
|
||||
call!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::SidekiqVersioning::Worker do
|
||||
let(:worker) do
|
||||
Class.new do
|
||||
def self.name
|
||||
'DummyWorker'
|
||||
end
|
||||
|
||||
# ApplicationWorker includes Gitlab::SidekiqVersioning::Worker
|
||||
include ApplicationWorker
|
||||
|
||||
version 2
|
||||
end
|
||||
end
|
||||
|
||||
describe '.version' do
|
||||
context 'when called with an argument' do
|
||||
it 'sets the version option' do
|
||||
worker.version 3
|
||||
|
||||
expect(worker.get_sidekiq_options['version']).to eq(3)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when called without an argument' do
|
||||
it 'returns the version option' do
|
||||
worker.sidekiq_options version: 3
|
||||
|
||||
expect(worker.version).to eq(3)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#job_version' do
|
||||
let(:job) { worker.new }
|
||||
|
||||
context 'when job_version is not set' do
|
||||
it 'returns latest version' do
|
||||
expect(job.job_version).to eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when job_version is set' do
|
||||
it 'returns the set version' do
|
||||
job.job_version = 0
|
||||
|
||||
expect(job.job_version).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -46,7 +46,7 @@ RSpec.describe ProjectPolicy do
|
|||
resolve_note create_container_image update_container_image destroy_container_image daily_statistics
|
||||
create_environment update_environment create_deployment update_deployment create_release update_release
|
||||
create_metrics_dashboard_annotation delete_metrics_dashboard_annotation update_metrics_dashboard_annotation
|
||||
read_terraform_state
|
||||
read_terraform_state read_pod_logs
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -17,49 +17,47 @@ RSpec.describe API::PypiPackages do
|
|||
|
||||
subject { get api(url) }
|
||||
|
||||
context 'without the need for a license' do
|
||||
context 'with valid project' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
context 'with valid project' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
|
||||
'PUBLIC' | :developer | true | true | 'PyPi package versions' | :success
|
||||
'PUBLIC' | :guest | true | true | 'PyPi package versions' | :success
|
||||
'PUBLIC' | :developer | true | false | 'PyPi package versions' | :success
|
||||
'PUBLIC' | :guest | true | false | 'PyPi package versions' | :success
|
||||
'PUBLIC' | :developer | false | true | 'PyPi package versions' | :success
|
||||
'PUBLIC' | :guest | false | true | 'PyPi package versions' | :success
|
||||
'PUBLIC' | :developer | false | false | 'PyPi package versions' | :success
|
||||
'PUBLIC' | :guest | false | false | 'PyPi package versions' | :success
|
||||
'PUBLIC' | :anonymous | false | true | 'PyPi package versions' | :success
|
||||
'PRIVATE' | :developer | true | true | 'PyPi package versions' | :success
|
||||
'PRIVATE' | :guest | true | true | 'process PyPi api request' | :forbidden
|
||||
'PRIVATE' | :developer | true | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :guest | true | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :developer | false | true | 'process PyPi api request' | :not_found
|
||||
'PRIVATE' | :guest | false | true | 'process PyPi api request' | :not_found
|
||||
'PRIVATE' | :developer | false | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :guest | false | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :anonymous | false | true | 'process PyPi api request' | :unauthorized
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:token) { user_token ? personal_access_token.token : 'wrong' }
|
||||
let(:headers) { user_role == :anonymous ? {} : build_basic_auth_header(user.username, token) }
|
||||
|
||||
subject { get api(url), headers: headers }
|
||||
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
|
||||
end
|
||||
|
||||
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
|
||||
end
|
||||
where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
|
||||
'PUBLIC' | :developer | true | true | 'PyPi package versions' | :success
|
||||
'PUBLIC' | :guest | true | true | 'PyPi package versions' | :success
|
||||
'PUBLIC' | :developer | true | false | 'PyPi package versions' | :success
|
||||
'PUBLIC' | :guest | true | false | 'PyPi package versions' | :success
|
||||
'PUBLIC' | :developer | false | true | 'PyPi package versions' | :success
|
||||
'PUBLIC' | :guest | false | true | 'PyPi package versions' | :success
|
||||
'PUBLIC' | :developer | false | false | 'PyPi package versions' | :success
|
||||
'PUBLIC' | :guest | false | false | 'PyPi package versions' | :success
|
||||
'PUBLIC' | :anonymous | false | true | 'PyPi package versions' | :success
|
||||
'PRIVATE' | :developer | true | true | 'PyPi package versions' | :success
|
||||
'PRIVATE' | :guest | true | true | 'process PyPi api request' | :forbidden
|
||||
'PRIVATE' | :developer | true | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :guest | true | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :developer | false | true | 'process PyPi api request' | :not_found
|
||||
'PRIVATE' | :guest | false | true | 'process PyPi api request' | :not_found
|
||||
'PRIVATE' | :developer | false | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :guest | false | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :anonymous | false | true | 'process PyPi api request' | :unauthorized
|
||||
end
|
||||
|
||||
it_behaves_like 'deploy token for package GET requests'
|
||||
with_them do
|
||||
let(:token) { user_token ? personal_access_token.token : 'wrong' }
|
||||
let(:headers) { user_role == :anonymous ? {} : build_basic_auth_header(user.username, token) }
|
||||
|
||||
it_behaves_like 'rejects PyPI access with unknown project id'
|
||||
subject { get api(url), headers: headers }
|
||||
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
|
||||
end
|
||||
|
||||
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'deploy token for package GET requests'
|
||||
|
||||
it_behaves_like 'rejects PyPI access with unknown project id'
|
||||
end
|
||||
|
||||
describe 'POST /api/v4/projects/:id/packages/pypi/authorize' do
|
||||
|
|
@ -70,48 +68,46 @@ RSpec.describe API::PypiPackages do
|
|||
|
||||
subject { post api(url), headers: headers }
|
||||
|
||||
context 'without the need for a license' do
|
||||
context 'with valid project' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
context 'with valid project' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
|
||||
'PUBLIC' | :developer | true | true | 'process PyPi api request' | :success
|
||||
'PUBLIC' | :guest | true | true | 'process PyPi api request' | :forbidden
|
||||
'PUBLIC' | :developer | true | false | 'process PyPi api request' | :unauthorized
|
||||
'PUBLIC' | :guest | true | false | 'process PyPi api request' | :unauthorized
|
||||
'PUBLIC' | :developer | false | true | 'process PyPi api request' | :forbidden
|
||||
'PUBLIC' | :guest | false | true | 'process PyPi api request' | :forbidden
|
||||
'PUBLIC' | :developer | false | false | 'process PyPi api request' | :unauthorized
|
||||
'PUBLIC' | :guest | false | false | 'process PyPi api request' | :unauthorized
|
||||
'PUBLIC' | :anonymous | false | true | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :developer | true | true | 'process PyPi api request' | :success
|
||||
'PRIVATE' | :guest | true | true | 'process PyPi api request' | :forbidden
|
||||
'PRIVATE' | :developer | true | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :guest | true | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :developer | false | true | 'process PyPi api request' | :not_found
|
||||
'PRIVATE' | :guest | false | true | 'process PyPi api request' | :not_found
|
||||
'PRIVATE' | :developer | false | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :guest | false | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :anonymous | false | true | 'process PyPi api request' | :unauthorized
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:token) { user_token ? personal_access_token.token : 'wrong' }
|
||||
let(:user_headers) { user_role == :anonymous ? {} : build_basic_auth_header(user.username, token) }
|
||||
let(:headers) { user_headers.merge(workhorse_header) }
|
||||
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
|
||||
end
|
||||
|
||||
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
|
||||
end
|
||||
where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
|
||||
'PUBLIC' | :developer | true | true | 'process PyPi api request' | :success
|
||||
'PUBLIC' | :guest | true | true | 'process PyPi api request' | :forbidden
|
||||
'PUBLIC' | :developer | true | false | 'process PyPi api request' | :unauthorized
|
||||
'PUBLIC' | :guest | true | false | 'process PyPi api request' | :unauthorized
|
||||
'PUBLIC' | :developer | false | true | 'process PyPi api request' | :forbidden
|
||||
'PUBLIC' | :guest | false | true | 'process PyPi api request' | :forbidden
|
||||
'PUBLIC' | :developer | false | false | 'process PyPi api request' | :unauthorized
|
||||
'PUBLIC' | :guest | false | false | 'process PyPi api request' | :unauthorized
|
||||
'PUBLIC' | :anonymous | false | true | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :developer | true | true | 'process PyPi api request' | :success
|
||||
'PRIVATE' | :guest | true | true | 'process PyPi api request' | :forbidden
|
||||
'PRIVATE' | :developer | true | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :guest | true | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :developer | false | true | 'process PyPi api request' | :not_found
|
||||
'PRIVATE' | :guest | false | true | 'process PyPi api request' | :not_found
|
||||
'PRIVATE' | :developer | false | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :guest | false | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :anonymous | false | true | 'process PyPi api request' | :unauthorized
|
||||
end
|
||||
|
||||
it_behaves_like 'deploy token for package uploads'
|
||||
with_them do
|
||||
let(:token) { user_token ? personal_access_token.token : 'wrong' }
|
||||
let(:user_headers) { user_role == :anonymous ? {} : build_basic_auth_header(user.username, token) }
|
||||
let(:headers) { user_headers.merge(workhorse_header) }
|
||||
|
||||
it_behaves_like 'rejects PyPI access with unknown project id'
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
|
||||
end
|
||||
|
||||
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'deploy token for package uploads'
|
||||
|
||||
it_behaves_like 'rejects PyPI access with unknown project id'
|
||||
end
|
||||
|
||||
describe 'POST /api/v4/projects/:id/packages/pypi' do
|
||||
|
|
@ -135,61 +131,59 @@ RSpec.describe API::PypiPackages do
|
|||
)
|
||||
end
|
||||
|
||||
context 'without the need for a license' do
|
||||
context 'with valid project' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
context 'with valid project' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
|
||||
'PUBLIC' | :developer | true | true | 'PyPi package creation' | :created
|
||||
'PUBLIC' | :guest | true | true | 'process PyPi api request' | :forbidden
|
||||
'PUBLIC' | :developer | true | false | 'process PyPi api request' | :unauthorized
|
||||
'PUBLIC' | :guest | true | false | 'process PyPi api request' | :unauthorized
|
||||
'PUBLIC' | :developer | false | true | 'process PyPi api request' | :forbidden
|
||||
'PUBLIC' | :guest | false | true | 'process PyPi api request' | :forbidden
|
||||
'PUBLIC' | :developer | false | false | 'process PyPi api request' | :unauthorized
|
||||
'PUBLIC' | :guest | false | false | 'process PyPi api request' | :unauthorized
|
||||
'PUBLIC' | :anonymous | false | true | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :developer | true | true | 'process PyPi api request' | :created
|
||||
'PRIVATE' | :guest | true | true | 'process PyPi api request' | :forbidden
|
||||
'PRIVATE' | :developer | true | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :guest | true | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :developer | false | true | 'process PyPi api request' | :not_found
|
||||
'PRIVATE' | :guest | false | true | 'process PyPi api request' | :not_found
|
||||
'PRIVATE' | :developer | false | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :guest | false | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :anonymous | false | true | 'process PyPi api request' | :unauthorized
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:token) { user_token ? personal_access_token.token : 'wrong' }
|
||||
let(:user_headers) { user_role == :anonymous ? {} : build_basic_auth_header(user.username, token) }
|
||||
let(:headers) { user_headers.merge(workhorse_header) }
|
||||
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
|
||||
end
|
||||
|
||||
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
|
||||
end
|
||||
where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
|
||||
'PUBLIC' | :developer | true | true | 'PyPi package creation' | :created
|
||||
'PUBLIC' | :guest | true | true | 'process PyPi api request' | :forbidden
|
||||
'PUBLIC' | :developer | true | false | 'process PyPi api request' | :unauthorized
|
||||
'PUBLIC' | :guest | true | false | 'process PyPi api request' | :unauthorized
|
||||
'PUBLIC' | :developer | false | true | 'process PyPi api request' | :forbidden
|
||||
'PUBLIC' | :guest | false | true | 'process PyPi api request' | :forbidden
|
||||
'PUBLIC' | :developer | false | false | 'process PyPi api request' | :unauthorized
|
||||
'PUBLIC' | :guest | false | false | 'process PyPi api request' | :unauthorized
|
||||
'PUBLIC' | :anonymous | false | true | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :developer | true | true | 'process PyPi api request' | :created
|
||||
'PRIVATE' | :guest | true | true | 'process PyPi api request' | :forbidden
|
||||
'PRIVATE' | :developer | true | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :guest | true | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :developer | false | true | 'process PyPi api request' | :not_found
|
||||
'PRIVATE' | :guest | false | true | 'process PyPi api request' | :not_found
|
||||
'PRIVATE' | :developer | false | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :guest | false | false | 'process PyPi api request' | :unauthorized
|
||||
'PRIVATE' | :anonymous | false | true | 'process PyPi api request' | :unauthorized
|
||||
end
|
||||
|
||||
context 'with an invalid package' do
|
||||
let(:token) { personal_access_token.token }
|
||||
let(:user_headers) { build_basic_auth_header(user.username, token) }
|
||||
with_them do
|
||||
let(:token) { user_token ? personal_access_token.token : 'wrong' }
|
||||
let(:user_headers) { user_role == :anonymous ? {} : build_basic_auth_header(user.username, token) }
|
||||
let(:headers) { user_headers.merge(workhorse_header) }
|
||||
|
||||
before do
|
||||
params[:name] = '.$/@!^*'
|
||||
project.add_developer(user)
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
|
||||
end
|
||||
|
||||
it_behaves_like 'returning response status', :bad_request
|
||||
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an invalid package' do
|
||||
let(:token) { personal_access_token.token }
|
||||
let(:user_headers) { build_basic_auth_header(user.username, token) }
|
||||
let(:headers) { user_headers.merge(workhorse_header) }
|
||||
|
||||
before do
|
||||
params[:name] = '.$/@!^*'
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
it_behaves_like 'deploy token for package uploads'
|
||||
|
||||
it_behaves_like 'rejects PyPI access with unknown project id'
|
||||
it_behaves_like 'returning response status', :bad_request
|
||||
end
|
||||
|
||||
it_behaves_like 'deploy token for package uploads'
|
||||
|
||||
it_behaves_like 'rejects PyPI access with unknown project id'
|
||||
end
|
||||
|
||||
describe 'GET /api/v4/projects/:id/packages/pypi/files/:sha256/*file_identifier' do
|
||||
|
|
@ -200,60 +194,58 @@ RSpec.describe API::PypiPackages do
|
|||
|
||||
subject { get api(url) }
|
||||
|
||||
context 'without the need for a license' do
|
||||
context 'with valid project' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
context 'with valid project' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
|
||||
'PUBLIC' | :developer | true | true | 'PyPi package download' | :success
|
||||
'PUBLIC' | :guest | true | true | 'PyPi package download' | :success
|
||||
'PUBLIC' | :developer | true | false | 'PyPi package download' | :success
|
||||
'PUBLIC' | :guest | true | false | 'PyPi package download' | :success
|
||||
'PUBLIC' | :developer | false | true | 'PyPi package download' | :success
|
||||
'PUBLIC' | :guest | false | true | 'PyPi package download' | :success
|
||||
'PUBLIC' | :developer | false | false | 'PyPi package download' | :success
|
||||
'PUBLIC' | :guest | false | false | 'PyPi package download' | :success
|
||||
'PUBLIC' | :anonymous | false | true | 'PyPi package download' | :success
|
||||
'PRIVATE' | :developer | true | true | 'PyPi package download' | :success
|
||||
'PRIVATE' | :guest | true | true | 'PyPi package download' | :success
|
||||
'PRIVATE' | :developer | true | false | 'PyPi package download' | :success
|
||||
'PRIVATE' | :guest | true | false | 'PyPi package download' | :success
|
||||
'PRIVATE' | :developer | false | true | 'PyPi package download' | :success
|
||||
'PRIVATE' | :guest | false | true | 'PyPi package download' | :success
|
||||
'PRIVATE' | :developer | false | false | 'PyPi package download' | :success
|
||||
'PRIVATE' | :guest | false | false | 'PyPi package download' | :success
|
||||
'PRIVATE' | :anonymous | false | true | 'PyPi package download' | :success
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:token) { user_token ? personal_access_token.token : 'wrong' }
|
||||
let(:headers) { user_role == :anonymous ? {} : build_basic_auth_header(user.username, token) }
|
||||
|
||||
subject { get api(url), headers: headers }
|
||||
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
|
||||
end
|
||||
|
||||
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
|
||||
end
|
||||
where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
|
||||
'PUBLIC' | :developer | true | true | 'PyPi package download' | :success
|
||||
'PUBLIC' | :guest | true | true | 'PyPi package download' | :success
|
||||
'PUBLIC' | :developer | true | false | 'PyPi package download' | :success
|
||||
'PUBLIC' | :guest | true | false | 'PyPi package download' | :success
|
||||
'PUBLIC' | :developer | false | true | 'PyPi package download' | :success
|
||||
'PUBLIC' | :guest | false | true | 'PyPi package download' | :success
|
||||
'PUBLIC' | :developer | false | false | 'PyPi package download' | :success
|
||||
'PUBLIC' | :guest | false | false | 'PyPi package download' | :success
|
||||
'PUBLIC' | :anonymous | false | true | 'PyPi package download' | :success
|
||||
'PRIVATE' | :developer | true | true | 'PyPi package download' | :success
|
||||
'PRIVATE' | :guest | true | true | 'PyPi package download' | :success
|
||||
'PRIVATE' | :developer | true | false | 'PyPi package download' | :success
|
||||
'PRIVATE' | :guest | true | false | 'PyPi package download' | :success
|
||||
'PRIVATE' | :developer | false | true | 'PyPi package download' | :success
|
||||
'PRIVATE' | :guest | false | true | 'PyPi package download' | :success
|
||||
'PRIVATE' | :developer | false | false | 'PyPi package download' | :success
|
||||
'PRIVATE' | :guest | false | false | 'PyPi package download' | :success
|
||||
'PRIVATE' | :anonymous | false | true | 'PyPi package download' | :success
|
||||
end
|
||||
|
||||
context 'with deploy token headers' do
|
||||
let(:headers) { build_basic_auth_header(deploy_token.username, deploy_token.token) }
|
||||
with_them do
|
||||
let(:token) { user_token ? personal_access_token.token : 'wrong' }
|
||||
let(:headers) { user_role == :anonymous ? {} : build_basic_auth_header(user.username, token) }
|
||||
|
||||
context 'valid token' do
|
||||
it_behaves_like 'returning response status', :success
|
||||
subject { get api(url), headers: headers }
|
||||
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
|
||||
end
|
||||
|
||||
context 'invalid token' do
|
||||
let(:headers) { build_basic_auth_header('foo', 'bar') }
|
||||
|
||||
it_behaves_like 'returning response status', :success
|
||||
end
|
||||
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
|
||||
end
|
||||
|
||||
it_behaves_like 'rejects PyPI access with unknown project id'
|
||||
end
|
||||
|
||||
context 'with deploy token headers' do
|
||||
let(:headers) { build_basic_auth_header(deploy_token.username, deploy_token.token) }
|
||||
|
||||
context 'valid token' do
|
||||
it_behaves_like 'returning response status', :success
|
||||
end
|
||||
|
||||
context 'invalid token' do
|
||||
let(:headers) { build_basic_auth_header('foo', 'bar') }
|
||||
|
||||
it_behaves_like 'returning response status', :success
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'rejects PyPI access with unknown project id'
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -83,9 +83,9 @@ RSpec.describe EnvironmentEntity do
|
|||
end
|
||||
|
||||
context 'pod_logs' do
|
||||
context 'with developer access' do
|
||||
context 'with reporter access' do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
project.add_reporter(user)
|
||||
end
|
||||
|
||||
it 'does not expose logs keys' do
|
||||
|
|
@ -95,9 +95,9 @@ RSpec.describe EnvironmentEntity do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with maintainer access' do
|
||||
context 'with developer access' do
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
it 'exposes logs keys' do
|
||||
|
|
|
|||
|
|
@ -1385,7 +1385,7 @@
|
|||
source-map "~0.6.1"
|
||||
vue-template-es2015-compiler "^1.9.0"
|
||||
|
||||
"@vue/test-utils@^1.0.0-beta.30":
|
||||
"@vue/test-utils@1.0.0-beta.30":
|
||||
version "1.0.0-beta.30"
|
||||
resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-1.0.0-beta.30.tgz#d5f26d1e2411fdb7fa7fdedb61b4b4ea4194c49d"
|
||||
integrity sha512-Wyvcha9fNk8+kzTDwb3xWGjPkCPzHSYSwKP6MplrPTG/auhqoad7JqUEceZLc6u7AU4km2pPQ8/m9s0RgCZ0NA==
|
||||
|
|
@ -12161,7 +12161,7 @@ vue-hot-reload-api@^2.3.0:
|
|||
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.0.tgz#97976142405d13d8efae154749e88c4e358cf926"
|
||||
integrity sha512-2j/t+wIbyVMP5NvctQoSUvLkYKoWAAk2QlQiilrM2a6/ulzFgdcLUJfTvs4XQ/3eZhHiBmmEojbjmM4AzZj8JA==
|
||||
|
||||
vue-jest@^4.0.0-beta.2:
|
||||
vue-jest@4.0.0-beta.2:
|
||||
version "4.0.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/vue-jest/-/vue-jest-4.0.0-beta.2.tgz#f2120ea9d24224aad3a100c2010b0760d47ee6fe"
|
||||
integrity sha512-SywBIciuIfqsCb8Eb9UQ02s06+NV8Ry8KnbyhAfnvnyFFulIuh7ujtga9eJYq720nCS4Hz4TpVtS4pD1ZbUILQ==
|
||||
|
|
|
|||
Loading…
Reference in New Issue