summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-05-06 18:09:38 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-05-06 18:09:38 +0000
commit73886079f3f877ffb8f8938d700643a5e99bc849 (patch)
treeff8f3c64df680962c1da38156e8c1e63a015b0fb
parenta7beadc83470bd9ce23757a019795f49f95a6fff (diff)
downloadgitlab-ce-73886079f3f877ffb8f8938d700643a5e99bc849.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/merge_request_templates/Documentation.md13
-rw-r--r--.rubocop.yml4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue2
-rw-r--r--app/assets/stylesheets/pages/issuable.scss13
-rw-r--r--app/assets/stylesheets/pages/labels.scss3
-rw-r--r--app/assets/stylesheets/pages/milestone.scss4
-rw-r--r--app/assets/stylesheets/pages/notes.scss3
-rw-r--r--app/views/import/google_code/new_user_map.html.haml2
-rw-r--r--app/views/shared/hook_logs/_content.html.haml2
-rw-r--r--app/workers/all_queues.yml2
-rw-r--r--app/workers/update_head_pipeline_for_merge_request_worker.rb4
-rw-r--r--changelogs/unreleased/199053-inconsistent-help-icon-styling.yml5
-rw-r--r--changelogs/unreleased/fj-update-snippet-backfilling-with-migrate-bot.yml5
-rw-r--r--danger/telemetry/Dangerfile16
-rw-r--r--db/migrate/20200429181335_add_default_value_for_file_store_to_lfs_objects.rb19
-rw-r--r--db/migrate/20200429181955_add_default_value_for_file_store_to_ci_job_artifacts.rb19
-rw-r--r--db/migrate/20200429182245_add_default_value_for_store_to_uploads.rb19
-rw-r--r--db/structure.sql9
-rw-r--r--doc/user/application_security/container_scanning/index.md28
-rw-r--r--doc/user/application_security/dast/index.md29
-rw-r--r--doc/user/application_security/dependency_scanning/index.md27
-rw-r--r--doc/user/application_security/sast/index.md16
-rw-r--r--doc/user/compliance/license_compliance/index.md14
-rw-r--r--lib/gitlab/background_migration/backfill_snippet_repositories.rb8
-rw-r--r--lib/gitlab/git_access_snippet.rb7
-rw-r--r--lib/gitlab/metrics/samplers/ruby_sampler.rb31
-rw-r--r--lib/gitlab/metrics/system.rb81
-rw-r--r--lib/gitlab/user_access_snippet.rb14
-rw-r--r--locale/gitlab.pot20
-rw-r--r--package.json2
-rw-r--r--spec/frontend/pipelines/header_component_spec.js116
-rw-r--r--spec/frontend/pipelines/pipelines_actions_spec.js142
-rw-r--r--spec/javascripts/pipelines/header_component_spec.js108
-rw-r--r--spec/javascripts/pipelines/pipelines_actions_spec.js128
-rw-r--r--spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb14
-rw-r--r--spec/lib/gitlab/git_access_snippet_spec.rb140
-rw-r--r--spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb17
-rw-r--r--spec/lib/gitlab/metrics/system_spec.rb113
-rw-r--r--spec/lib/gitlab/user_access_snippet_spec.rb52
-rw-r--r--spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb65
-rw-r--r--yarn.lock8
41 files changed, 767 insertions, 557 deletions
diff --git a/.gitlab/merge_request_templates/Documentation.md b/.gitlab/merge_request_templates/Documentation.md
index 7e9a0b7133a..72bfd2cdec4 100644
--- a/.gitlab/merge_request_templates/Documentation.md
+++ b/.gitlab/merge_request_templates/Documentation.md
@@ -16,12 +16,13 @@
## Author's checklist (required)
- [ ] Follow the [Documentation Guidelines](https://docs.gitlab.com/ee/development/documentation/) and [Style Guide](https://docs.gitlab.com/ee/development/documentation/styleguide.html).
-- [ ] Apply the ~documentation label, plus:
- - The corresponding DevOps stage and group label, if applicable.
- - ~"development guidelines" when changing docs under `doc/development/*`, `CONTRIBUTING.md`, or `README.md`.
- - ~"development guidelines" and ~"Documentation guidelines" when changing docs under `development/documentation/*`.
- - ~"development guidelines" and ~"Description templates (.gitlab/\*)" when creating/updating issue and MR description templates.
-- [ ] Assign the [designated Technical Writer](https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments).
+- If you have `developer` access or higher (for example, GitLab team members or [Core Team](https://about.gitlab.com/community/core-team/) members)
+ - [ ] Apply the ~documentation label, plus:
+ - The corresponding DevOps stage and group label, if applicable.
+ - ~"development guidelines" when changing docs under `doc/development/*`, `CONTRIBUTING.md`, or `README.md`.
+ - ~"development guidelines" and ~"Documentation guidelines" when changing docs under `development/documentation/*`.
+ - ~"development guidelines" and ~"Description templates (.gitlab/\*)" when creating/updating issue and MR description templates.
+ - [ ] Assign the [designated Technical Writer](https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments).
When applicable:
diff --git a/.rubocop.yml b/.rubocop.yml
index fbcad46a0c7..92861717cab 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -212,11 +212,13 @@ Gitlab/HTTParty:
- 'ee/spec/**/*'
Gitlab/Json:
- Enabled: false
+ Enabled: true
Exclude:
- 'db/**/*'
- 'qa/**/*'
- 'scripts/**/*'
+ - 'lib/rspec_flaky/**/*'
+ - 'lib/quality/**/*'
GitlabSecurity/PublicSend:
Enabled: true
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue
index 98f682c2e8a..5305894873f 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue
@@ -51,7 +51,7 @@ export default {
rel="noopener noreferrer nofollow"
data-container="body"
>
- <icon name="question-o" />
+ <icon name="question" />
</a>
</div>
</template>
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 85fdcb753b4..b241d0a2bdc 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -167,10 +167,6 @@
}
}
- a.gl-label-icon {
- color: $gray-500;
- }
-
.gl-label .gl-label-link:hover {
text-decoration: none;
color: inherit;
@@ -180,11 +176,6 @@
}
}
- .gl-label .gl-label-icon:hover {
- text-decoration: none;
- color: $gray-500;
- }
-
.btn-link {
color: inherit;
}
@@ -826,10 +817,6 @@
}
}
}
-
- .gl-label-icon {
- color: $gray-500;
- }
}
@media(max-width: map-get($grid-breakpoints, lg)-1) {
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index b42f0195f69..22c1cb127cd 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -460,8 +460,7 @@
// Label inside title of Delete Label Modal
.modal-header .page-title {
.scoped-label-wrapper {
- .scoped-label,
- .gl-label-icon {
+ .scoped-label {
line-height: 20px;
}
diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss
index fa10ab022dc..c473cc44637 100644
--- a/app/assets/stylesheets/pages/milestone.scss
+++ b/app/assets/stylesheets/pages/milestone.scss
@@ -68,10 +68,6 @@ $status-box-line-height: 26px;
.gl-label-link {
color: inherit;
}
-
- .gl-label-icon {
- color: $gray-500;
- }
}
}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 6445695bc6b..bed147aa3a7 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -688,8 +688,7 @@ $note-form-margin-left: 72px;
text-decoration: underline;
}
- .gl-label-link:hover,
- .gl-label-icon:hover {
+ .gl-label-link:hover {
text-decoration: none;
color: inherit;
diff --git a/app/views/import/google_code/new_user_map.html.haml b/app/views/import/google_code/new_user_map.html.haml
index f523b993aa7..732ba95a63f 100644
--- a/app/views/import/google_code/new_user_map.html.haml
+++ b/app/views/import/google_code/new_user_map.html.haml
@@ -30,7 +30,7 @@
.form-group.row
.col-sm-12
- = text_area_tag :user_map, JSON.pretty_generate(@user_map), class: 'form-control', rows: 15
+ = text_area_tag :user_map, Gitlab::Json.pretty_generate(@user_map), class: 'form-control', rows: 15
.form-actions
= submit_tag _('Continue to the next step'), class: "btn btn-success"
diff --git a/app/views/shared/hook_logs/_content.html.haml b/app/views/shared/hook_logs/_content.html.haml
index f3b56df0c96..6b056e93460 100644
--- a/app/views/shared/hook_logs/_content.html.haml
+++ b/app/views/shared/hook_logs/_content.html.haml
@@ -31,7 +31,7 @@
%h5 Request body:
%pre
:escaped
- #{JSON.pretty_generate(hook_log.request_data)}
+ #{Gitlab::Json.pretty_generate(hook_log.request_data)}
%h5 Response headers:
%pre
- hook_log.response_headers.each do |k,v|
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index ccac944605d..6809d818717 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -849,7 +849,7 @@
:urgency: :high
:resource_boundary: :cpu
:weight: 5
- :idempotent:
+ :idempotent: true
- :name: repository_check:repository_check_batch
:feature_category: :source_code_management
:has_external_dependencies:
diff --git a/app/workers/update_head_pipeline_for_merge_request_worker.rb b/app/workers/update_head_pipeline_for_merge_request_worker.rb
index 69698ba81bd..63d11d33283 100644
--- a/app/workers/update_head_pipeline_for_merge_request_worker.rb
+++ b/app/workers/update_head_pipeline_for_merge_request_worker.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class UpdateHeadPipelineForMergeRequestWorker # rubocop:disable Scalability/IdempotentWorker
+class UpdateHeadPipelineForMergeRequestWorker
include ApplicationWorker
include PipelineQueue
@@ -9,6 +9,8 @@ class UpdateHeadPipelineForMergeRequestWorker # rubocop:disable Scalability/Idem
urgency :high
worker_resource_boundary :cpu
+ idempotent!
+
def perform(merge_request_id)
MergeRequest.find_by_id(merge_request_id).try do |merge_request|
merge_request.update_head_pipeline
diff --git a/changelogs/unreleased/199053-inconsistent-help-icon-styling.yml b/changelogs/unreleased/199053-inconsistent-help-icon-styling.yml
new file mode 100644
index 00000000000..3d11351ca8b
--- /dev/null
+++ b/changelogs/unreleased/199053-inconsistent-help-icon-styling.yml
@@ -0,0 +1,5 @@
+---
+title: Update merge request widget question mark icons
+merge_request: 30759
+author:
+type: other
diff --git a/changelogs/unreleased/fj-update-snippet-backfilling-with-migrate-bot.yml b/changelogs/unreleased/fj-update-snippet-backfilling-with-migrate-bot.yml
new file mode 100644
index 00000000000..07030a44875
--- /dev/null
+++ b/changelogs/unreleased/fj-update-snippet-backfilling-with-migrate-bot.yml
@@ -0,0 +1,5 @@
+---
+title: Use migration bot user in snippet migration
+merge_request: 30762
+author:
+type: fixed
diff --git a/danger/telemetry/Dangerfile b/danger/telemetry/Dangerfile
index dd3c8a6a322..c18a15fcb03 100644
--- a/danger/telemetry/Dangerfile
+++ b/danger/telemetry/Dangerfile
@@ -9,12 +9,24 @@ USAGE_DATA_FILES_MESSAGE = <<~MSG
For the following files, a review from the [Data team and Telemetry team](https://gitlab.com/groups/gitlab-org/growth/telemetry/engineers/-/group_members?with_inherited_permissions=exclude) is recommended:
MSG
+tracking_files = [
+ 'lib/gitlab/tracking.rb',
+ 'spec/lib/gitlab/tracking_spec.rb',
+ 'app/helpers/tracking_helper.rb',
+ 'spec/helpers/tracking_helper_spec.rb',
+ 'app/assets/javascripts/tracking.js',
+ 'spec/frontend/tracking_spec.js'
+ ]
+
usage_data_changed_files = git.modified_files.grep(%r{usage_data})
+snowplow_events_changed_files = git.modified_files & tracking_files
+
+changed_files = (usage_data_changed_files + snowplow_events_changed_files)
-if usage_data_changed_files.any?
+if changed_files.any?
warn format(TELEMETRY_CHANGED_FILES_MESSAGE)
- markdown(USAGE_DATA_FILES_MESSAGE + helper.markdown_list(usage_data_changed_files))
+ markdown(USAGE_DATA_FILES_MESSAGE + helper.markdown_list(changed_files))
telemetry_labels = ['telemetry']
telemetry_labels << 'telemetry::review pending' unless helper.mr_has_labels?('telemetry::reviewed')
diff --git a/db/migrate/20200429181335_add_default_value_for_file_store_to_lfs_objects.rb b/db/migrate/20200429181335_add_default_value_for_file_store_to_lfs_objects.rb
new file mode 100644
index 00000000000..f316a092bfc
--- /dev/null
+++ b/db/migrate/20200429181335_add_default_value_for_file_store_to_lfs_objects.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddDefaultValueForFileStoreToLfsObjects < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ with_lock_retries do
+ change_column_default :lfs_objects, :file_store, 1
+ end
+ end
+
+ def down
+ with_lock_retries do
+ change_column_default :lfs_objects, :file_store, nil
+ end
+ end
+end
diff --git a/db/migrate/20200429181955_add_default_value_for_file_store_to_ci_job_artifacts.rb b/db/migrate/20200429181955_add_default_value_for_file_store_to_ci_job_artifacts.rb
new file mode 100644
index 00000000000..ac3d5e41e3e
--- /dev/null
+++ b/db/migrate/20200429181955_add_default_value_for_file_store_to_ci_job_artifacts.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddDefaultValueForFileStoreToCiJobArtifacts < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ with_lock_retries do
+ change_column_default :ci_job_artifacts, :file_store, 1
+ end
+ end
+
+ def down
+ with_lock_retries do
+ change_column_default :ci_job_artifacts, :file_store, nil
+ end
+ end
+end
diff --git a/db/migrate/20200429182245_add_default_value_for_store_to_uploads.rb b/db/migrate/20200429182245_add_default_value_for_store_to_uploads.rb
new file mode 100644
index 00000000000..f28fcce8f2f
--- /dev/null
+++ b/db/migrate/20200429182245_add_default_value_for_store_to_uploads.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddDefaultValueForStoreToUploads < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ with_lock_retries do
+ change_column_default :uploads, :store, 1
+ end
+ end
+
+ def down
+ with_lock_retries do
+ change_column_default :uploads, :store, nil
+ end
+ end
+end
diff --git a/db/structure.sql b/db/structure.sql
index 73edcc371ce..e450a99eace 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -1120,7 +1120,7 @@ CREATE TABLE public.ci_job_artifacts (
updated_at timestamp with time zone NOT NULL,
expire_at timestamp with time zone,
file character varying,
- file_store integer,
+ file_store integer DEFAULT 1,
file_sha256 bytea,
file_format smallint,
file_location smallint,
@@ -3644,7 +3644,7 @@ CREATE TABLE public.lfs_objects (
created_at timestamp without time zone,
updated_at timestamp without time zone,
file character varying,
- file_store integer
+ file_store integer DEFAULT 1
);
CREATE SEQUENCE public.lfs_objects_id_seq
@@ -6487,7 +6487,7 @@ CREATE TABLE public.uploads (
model_type character varying,
uploader character varying NOT NULL,
created_at timestamp without time zone NOT NULL,
- store integer,
+ store integer DEFAULT 1,
mount_point character varying,
secret character varying
);
@@ -13706,5 +13706,8 @@ COPY "schema_migrations" (version) FROM STDIN;
20200424101920
20200427064130
20200429015603
+20200429181335
+20200429181955
+20200429182245
\.
diff --git a/doc/user/application_security/container_scanning/index.md b/doc/user/application_security/container_scanning/index.md
index 3eb7467b410..76dfcc901fd 100644
--- a/doc/user/application_security/container_scanning/index.md
+++ b/doc/user/application_security/container_scanning/index.md
@@ -229,25 +229,29 @@ To use Container Scanning in an offline environment, you need:
NOTE: **Note:**
GitLab Runner has a [default `pull policy` of `always`](https://docs.gitlab.com/runner/executors/docker.html#using-the-always-pull-policy),
-meaning the runner may try to pull remote images even if a local copy is available. Set GitLab
-Runner's [`pull_policy` to `if-not-present`](https://docs.gitlab.com/runner/executors/docker.html#using-the-if-not-present-pull-policy)
-in an offline environment if you prefer using only locally available Docker images.
+meaning the Runner tries to pull Docker images from the GitLab container registry even if a local
+copy is available. GitLab Runner's [`pull_policy` can be set to `if-not-present`](https://docs.gitlab.com/runner/executors/docker.html#using-the-if-not-present-pull-policy)
+in an offline environment if you prefer using only locally available Docker images. However, we
+recommend keeping the pull policy setting to `always` if not in an offline environment, as this
+enables the use of updated scanners in your CI/CD pipelines.
#### Make GitLab Container Scanning analyzer images available inside your Docker registry
-For Container Scanning, import and host the following images from `registry.gitlab.com` to your
-offline [local Docker container registry](../../packages/container_registry/index.md):
+For Container Scanning, import the following default images from `registry.gitlab.com` into your
+[local Docker container registry](../../packages/container_registry/index.md):
-- [arminc/clair-db vulnerabilities database](https://hub.docker.com/r/arminc/clair-db)
-- GitLab klar analyzer: `registry.gitlab.com/gitlab-org/security-products/analyzers/klar`
+```plaintext
+registry.gitlab.com/gitlab-org/security-products/analyzers/klar
+https://hub.docker.com/r/arminc/clair-db
+```
The process for importing Docker images into a local offline Docker registry depends on
**your network security policy**. Please consult your IT staff to find an accepted and approved
-process by which external resources can be imported or temporarily accessed.
-
-Note that these scanners are [updated periodically](../index.md#maintenance-and-update-of-the-vulnerabilities-database)
+process by which you can import or temporarily access external resources. Note that these scanners
+are [updated periodically](../index.md#maintenance-and-update-of-the-vulnerabilities-database)
with new definitions, so consider if you are able to make periodic updates yourself.
-You can read more specific steps on how to do this [below](#automating-container-scanning-vulnerability-database-updates-with-a-pipeline).
+
+For more information, see [the specific steps on how to update an image with a pipeline](#automating-container-scanning-vulnerability-database-updates-with-a-pipeline).
For details on saving and transporting Docker images as a file, see Docker's documentation on
[`docker save`](https://docs.docker.com/engine/reference/commandline/save/), [`docker load`](https://docs.docker.com/engine/reference/commandline/load/),
@@ -255,8 +259,6 @@ For details on saving and transporting Docker images as a file, see Docker's doc
#### Set Container Scanning CI job variables to use local Container Scanner analyzers
-Container Scanning can be executed on an offline GitLab Ultimate installation using the following process:
-
1. [Override the container scanning template](#overriding-the-container-scanning-template) in your `.gitlab-ci.yml` file to refer to the Docker images hosted on your local Docker container registry:
```yaml
diff --git a/doc/user/application_security/dast/index.md b/doc/user/application_security/dast/index.md
index 42480e60e48..15ce6695b4f 100644
--- a/doc/user/application_security/dast/index.md
+++ b/doc/user/application_security/dast/index.md
@@ -523,14 +523,15 @@ To use DAST in an offline environment, you need:
NOTE: **Note:**
GitLab Runner has a [default `pull policy` of `always`](https://docs.gitlab.com/runner/executors/docker.html#using-the-always-pull-policy),
-meaning the runner may try to pull remote images even if a local copy is available. Set GitLab
-Runner's [`pull_policy` to `if-not-present`](https://docs.gitlab.com/runner/executors/docker.html#using-the-if-not-present-pull-policy)
-in an offline environment if you prefer using only locally available Docker images.
+meaning the Runner tries to pull Docker images from the GitLab container registry even if a local
+copy is available. GitLab Runner's [`pull_policy` can be set to `if-not-present`](https://docs.gitlab.com/runner/executors/docker.html#using-the-if-not-present-pull-policy)
+in an offline environment if you prefer using only locally available Docker images. However, we
+recommend keeping the pull policy setting to `always` if not in an offline environment, as this
+enables the use of updated scanners in your CI/CD pipelines.
### Make GitLab DAST analyzer images available inside your Docker registry
-For DAST, import the following default DAST analyzer image from `registry.gitlab.com` to your local "offline"
-registry:
+For DAST, import the following default DAST analyzer image from `registry.gitlab.com` to your [local Docker container registry](../../packages/container_registry/index.md):
- `registry.gitlab.com/gitlab-org/security-products/dast:latest`
@@ -548,16 +549,18 @@ For details on saving and transporting Docker images as a file, see Docker's doc
### Set DAST CI job variables to use local DAST analyzers
-1. Add the following configuration to your `.gitlab-ci.yml` file. You must replace `image` to refer
- to the DAST Docker image hosted on your local Docker container registry:
+Add the following configuration to your `.gitlab-ci.yml` file. You must replace `image` to refer to
+the DAST Docker image hosted on your local Docker container registry:
- ```yaml
- include:
- - template: DAST.gitlab-ci.yml
+```yaml
+include:
+ - template: DAST.gitlab-ci.yml
+dast:
+ image: registry.example.com/namespace/dast:latest
+```
- dast:
- image: registry.example.com/namespace/dast:latest
- ```
+The DAST job should now use local copies of the DAST analyzers to scan your code and generate
+security reports without requiring internet access.
## Reports
diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md
index be9c8c9d129..99f4d524b7d 100644
--- a/doc/user/application_security/dependency_scanning/index.md
+++ b/doc/user/application_security/dependency_scanning/index.md
@@ -420,32 +420,33 @@ You can also [submit new vulnerabilities](https://gitlab.com/gitlab-org/security
## Running Dependency Scanning in an offline environment
For self-managed GitLab instances in an environment with limited, restricted, or intermittent access
-to external resources through the internet, some adjustments are required for dependency scanning jobs to run successfully. For more information, see [Offline environments](../offline_deployments/index.md).
+to external resources through the internet, some adjustments are required for Dependency Scanning
+jobs to run successfully. For more information, see [Offline environments](../offline_deployments/index.md).
### Requirements for offline Dependency Scanning
-The requirements for using Dependency Scanning in an offline environment are:
+Here are the requirements for using Dependency Scanning in an offline environment:
- [Disable Docker-In-Docker](#disabling-docker-in-docker-for-dependency-scanning).
- GitLab Runner with the [`docker` or `kubernetes` executor](#requirements).
-- Docker Container Registry with locally available copies of dependency scanning [analyzer](https://gitlab.com/gitlab-org/security-products/analyzers) images.
+- Docker Container Registry with locally available copies of Dependency Scanning [analyzer](https://gitlab.com/gitlab-org/security-products/analyzers) images.
- Host an offline Git copy of the [gemnasium-db advisory database](https://gitlab.com/gitlab-org/security-products/gemnasium-db/)
- _Only if scanning Ruby projects_: Host an offline Git copy of the [advisory database](https://github.com/rubysec/ruby-advisory-db).
- _Only if scanning npm/yarn projects_: Host an offline copy of the [retire.js](https://github.com/RetireJS/retire.js/) [node](https://github.com/RetireJS/retire.js/blob/master/repository/npmrepository.json) and [js](https://github.com/RetireJS/retire.js/blob/master/repository/jsrepository.json) advisory databases.
NOTE: **Note:**
GitLab Runner has a [default `pull policy` of `always`](https://docs.gitlab.com/runner/executors/docker.html#using-the-always-pull-policy),
-meaning the runner will try to pull Docker images from the GitLab container registry even if a local
+meaning the Runner tries to pull Docker images from the GitLab container registry even if a local
copy is available. GitLab Runner's [`pull_policy` can be set to `if-not-present`](https://docs.gitlab.com/runner/executors/docker.html#using-the-if-not-present-pull-policy)
-in an offline environment, if you prefer using only locally available Docker images. However, we
-recommend keeping the pull policy setting to `always` as it will better enable updated scanners to
-be utilized within your CI/CD pipelines.
+in an offline environment if you prefer using only locally available Docker images. However, we
+recommend keeping the pull policy setting to `always` if not in an offline environment, as this
+enables the use of updated scanners in your CI/CD pipelines.
### Make GitLab Dependency Scanning analyzer images available inside your Docker registry
-For Dependency Scanning, import Docker images ([supported languages and frameworks](#supported-languages-and-package-managers))
-from `registry.gitlab.com` to your offline Docker registry. The Dependency Scanning analyzer
-Docker images are:
+For Dependency Scanning with all [supported languages and frameworks](#supported-languages-and-package-managers),
+import the following default Dependency Scanning analyzer images from `registry.gitlab.com` into
+your [local Docker container registry](../../packages/container_registry/index.md):
```plaintext
registry.gitlab.com/gitlab-org/security-products/analyzers/gemnasium:2
@@ -465,10 +466,10 @@ For details on saving and transporting Docker images as a file, see Docker's doc
[`docker save`](https://docs.docker.com/engine/reference/commandline/save/), [`docker load`](https://docs.docker.com/engine/reference/commandline/load/),
[`docker export`](https://docs.docker.com/engine/reference/commandline/export/), and [`docker import`](https://docs.docker.com/engine/reference/commandline/import/).
-### Set Dependency Scanning CI config for "offline" use
+### Set Dependency Scanning CI job variables to use local Dependency Scanning analyzers
-Below is a general `.gitlab-ci.yml` template to configure your environment for running
-Dependency Scanning offline:
+Add the following configuration to your `.gitlab-ci.yml` file. You must replace
+`DS_ANALYZER_IMAGE_PREFIX` to refer to your local Docker container registry:
```yaml
include:
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index 51a2fdab1b9..0f42e062901 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -527,17 +527,17 @@ To use SAST in an offline environment, you need:
NOTE: **Note:**
GitLab Runner has a [default `pull policy` of `always`](https://docs.gitlab.com/runner/executors/docker.html#using-the-always-pull-policy),
-meaning the runner will try to pull Docker images from the GitLab container registry even if a local
+meaning the Runner tries to pull Docker images from the GitLab container registry even if a local
copy is available. GitLab Runner's [`pull_policy` can be set to `if-not-present`](https://docs.gitlab.com/runner/executors/docker.html#using-the-if-not-present-pull-policy)
in an offline environment if you prefer using only locally available Docker images. However, we
-recommend keeping the pull policy setting to `always` as it will better enable updated scanners to
-be utilized within your CI/CD pipelines.
+recommend keeping the pull policy setting to `always` if not in an offline environment, as this
+enables the use of updated scanners in your CI/CD pipelines.
### Make GitLab SAST analyzer images available inside your Docker registry
For SAST with all [supported languages and frameworks](#supported-languages-and-frameworks),
-import the following default SAST analyzer images from `registry.gitlab.com` to your local "offline"
-registry:
+import the following default SAST analyzer images from `registry.gitlab.com` into your
+[local Docker container registry](../../packages/container_registry/index.md):
```plaintext
registry.gitlab.com/gitlab-org/security-products/analyzers/bandit:2
@@ -568,10 +568,8 @@ For details on saving and transporting Docker images as a file, see Docker's doc
### Set SAST CI job variables to use local SAST analyzers
-[Override SAST environment variables](#customizing-the-sast-settings) to use to your [local container registry](./analyzers.md#using-a-custom-docker-mirror)
-as the source for SAST analyzer images.
-
-For example, assuming a local Docker registry repository of `localhost:5000/analyzers`:
+Add the following configuration to your `.gitlab-ci.yml` file. You must replace
+`SAST_ANALYZER_IMAGE_PREFIX` to refer to your local Docker container registry:
```yaml
include:
diff --git a/doc/user/compliance/license_compliance/index.md b/doc/user/compliance/license_compliance/index.md
index 3e468cbb607..9f77b3baad0 100644
--- a/doc/user/compliance/license_compliance/index.md
+++ b/doc/user/compliance/license_compliance/index.md
@@ -333,7 +333,7 @@ license_scanning:
For self-managed GitLab instances in an environment with limited, restricted, or intermittent access
to external resources through the internet, some adjustments are required for the License Compliance job to
-successfully run.
+successfully run. For more information, see [Offline environments](../../application_security/offline_deployments/index.md).
### Requirements for offline License Compliance
@@ -344,11 +344,11 @@ To use License Compliance in an offline environment, you need:
NOTE: **Note:**
GitLab Runner has a [default `pull policy` of `always`](https://docs.gitlab.com/runner/executors/docker.html#using-the-always-pull-policy),
-meaning the runner will try to pull Docker images from the GitLab container registry even if a local
+meaning the Runner tries to pull Docker images from the GitLab container registry even if a local
copy is available. GitLab Runner's [`pull_policy` can be set to `if-not-present`](https://docs.gitlab.com/runner/executors/docker.html#using-the-if-not-present-pull-policy)
in an offline environment if you prefer using only locally available Docker images. However, we
-recommend leaving the pull policy set to `always`, as it better enables updated scanners to be used
-within your CI/CD pipelines.
+recommend keeping the pull policy setting to `always` if not in an offline environment, as this
+enables the use of updated scanners in your CI/CD pipelines.
### Make GitLab License Compliance analyzer images available inside your Docker registry
@@ -371,10 +371,8 @@ For details on saving and transporting Docker images as a file, see Docker's doc
### Set License Compliance CI job variables to use local License Compliance analyzers
-Override License Compliance environment variables to use to your local container registry
-as the source for License Compliance analyzer images.
-
-For example, this assumes a local Docker registry repository of `localhost:5000/analyzers`:
+Add the following configuration to your `.gitlab-ci.yml` file. You must replace `image` to refer to
+the License Compliance Docker image hosted on your local Docker container registry:
```yaml
include:
diff --git a/lib/gitlab/background_migration/backfill_snippet_repositories.rb b/lib/gitlab/background_migration/backfill_snippet_repositories.rb
index 7aa00e03dda..192a82c84e6 100644
--- a/lib/gitlab/background_migration/backfill_snippet_repositories.rb
+++ b/lib/gitlab/background_migration/backfill_snippet_repositories.rb
@@ -98,17 +98,17 @@ module Gitlab
# because it is blocked, internal, ghost, ... we cannot commit
# files because these users are not allowed to, but we need to
# migrate their snippets as well.
- # In this scenario an admin user will be the one that will commit the files.
+ # In this scenario the migration bot user will be the one that will commit the files.
def commit_author(snippet)
if Gitlab::UserAccessSnippet.new(snippet.author, snippet: snippet).can_do_action?(:update_snippet)
snippet.author
else
- admin_user
+ migration_bot_user
end
end
- def admin_user
- @admin_user ||= User.admins.active.first
+ def migration_bot_user
+ @migration_bot_user ||= User.migration_bot
end
# We sometimes receive invalid path errors from Gitaly if the Snippet filename
diff --git a/lib/gitlab/git_access_snippet.rb b/lib/gitlab/git_access_snippet.rb
index d05ffe9b508..a06eddab78f 100644
--- a/lib/gitlab/git_access_snippet.rb
+++ b/lib/gitlab/git_access_snippet.rb
@@ -61,6 +61,13 @@ module Gitlab
end
end
+ override :can_read_project?
+ def can_read_project?
+ return true if user&.migration_bot?
+
+ super
+ end
+
override :check_download_access!
def check_download_access!
passed = guest_can_download_code? || user_can_download_code?
diff --git a/lib/gitlab/metrics/samplers/ruby_sampler.rb b/lib/gitlab/metrics/samplers/ruby_sampler.rb
index 5cd2a86a106..c38769f39a9 100644
--- a/lib/gitlab/metrics/samplers/ruby_sampler.rb
+++ b/lib/gitlab/metrics/samplers/ruby_sampler.rb
@@ -34,16 +34,14 @@ module Gitlab
def init_metrics
metrics = {
- file_descriptors: ::Gitlab::Metrics.gauge(with_prefix(:file, :descriptors), 'File descriptors used', labels),
- memory_bytes: ::Gitlab::Metrics.gauge(with_prefix(:memory, :bytes), 'Memory used (RSS)', labels),
- process_cpu_seconds_total: ::Gitlab::Metrics.gauge(with_prefix(:process, :cpu_seconds_total), 'Process CPU seconds total'),
- process_max_fds: ::Gitlab::Metrics.gauge(with_prefix(:process, :max_fds), 'Process max fds'),
- process_resident_memory_bytes: ::Gitlab::Metrics.gauge(with_prefix(:process, :resident_memory_bytes), 'Memory used (RSS)', labels),
- process_unique_memory_bytes: ::Gitlab::Metrics.gauge(with_prefix(:process, :unique_memory_bytes), 'Memory used (USS)', labels),
- process_proportional_memory_bytes: ::Gitlab::Metrics.gauge(with_prefix(:process, :proportional_memory_bytes), 'Memory used (PSS)', labels),
- process_start_time_seconds: ::Gitlab::Metrics.gauge(with_prefix(:process, :start_time_seconds), 'Process start time seconds'),
- sampler_duration: ::Gitlab::Metrics.counter(with_prefix(:sampler, :duration_seconds_total), 'Sampler time', labels),
- gc_duration_seconds: ::Gitlab::Metrics.histogram(with_prefix(:gc, :duration_seconds), 'GC time', labels, GC_REPORT_BUCKETS)
+ file_descriptors: ::Gitlab::Metrics.gauge(with_prefix(:file, :descriptors), 'File descriptors used', labels),
+ memory_bytes: ::Gitlab::Metrics.gauge(with_prefix(:memory, :bytes), 'Memory used', labels),
+ process_cpu_seconds_total: ::Gitlab::Metrics.gauge(with_prefix(:process, :cpu_seconds_total), 'Process CPU seconds total'),
+ process_max_fds: ::Gitlab::Metrics.gauge(with_prefix(:process, :max_fds), 'Process max fds'),
+ process_resident_memory_bytes: ::Gitlab::Metrics.gauge(with_prefix(:process, :resident_memory_bytes), 'Memory used', labels),
+ process_start_time_seconds: ::Gitlab::Metrics.gauge(with_prefix(:process, :start_time_seconds), 'Process start time seconds'),
+ sampler_duration: ::Gitlab::Metrics.counter(with_prefix(:sampler, :duration_seconds_total), 'Sampler time', labels),
+ gc_duration_seconds: ::Gitlab::Metrics.histogram(with_prefix(:gc, :duration_seconds), 'GC time', labels, GC_REPORT_BUCKETS)
}
GC.stat.keys.each do |key|
@@ -87,15 +85,10 @@ module Gitlab
end
def set_memory_usage_metrics
- memory_rss = System.memory_usage
- metrics[:memory_bytes].set(labels, memory_rss)
- metrics[:process_resident_memory_bytes].set(labels, memory_rss)
-
- if Feature.enabled?(:collect_memory_uss_pss)
- memory_uss_pss = System.memory_usage_uss_pss
- metrics[:process_unique_memory_bytes].set(labels, memory_uss_pss[:uss])
- metrics[:process_proportional_memory_bytes].set(labels, memory_uss_pss[:pss])
- end
+ memory_usage = System.memory_usage
+
+ metrics[:memory_bytes].set(labels, memory_usage)
+ metrics[:process_resident_memory_bytes].set(labels, memory_usage)
end
end
end
diff --git a/lib/gitlab/metrics/system.rb b/lib/gitlab/metrics/system.rb
index d01b6bc5b50..2a61b3de405 100644
--- a/lib/gitlab/metrics/system.rb
+++ b/lib/gitlab/metrics/system.rb
@@ -7,37 +7,47 @@ module Gitlab
# This module relies on the /proc filesystem being available. If /proc is
# not available the methods of this module will be stubbed.
module System
- PROC_STATUS_PATH = '/proc/self/status'
- PROC_SMAPS_ROLLUP_PATH = '/proc/self/smaps_rollup'
- PROC_LIMITS_PATH = '/proc/self/limits'
- PROC_FD_GLOB = '/proc/self/fd/*'
-
- PRIVATE_PAGES_PATTERN = /^(Private_Clean|Private_Dirty|Private_Hugetlb):\s+(?<value>\d+)/.freeze
- PSS_PATTERN = /^Pss:\s+(?<value>\d+)/.freeze
- RSS_PATTERN = /VmRSS:\s+(?<value>\d+)/.freeze
- MAX_OPEN_FILES_PATTERN = /Max open files\s*(?<value>\d+)/.freeze
-
- # Returns the current process' RSS (resident set size) in bytes.
- def self.memory_usage
- sum_matches(PROC_STATUS_PATH, rss: RSS_PATTERN)[:rss].kilobytes
- end
+ if File.exist?('/proc')
+ # Returns the current process' memory usage in bytes.
+ def self.memory_usage
+ mem = 0
+ match = File.read('/proc/self/status').match(/VmRSS:\s+(\d+)/)
+
+ if match && match[1]
+ mem = match[1].to_f * 1024
+ end
- # Returns the current process' USS/PSS (unique/proportional set size) in bytes.
- def self.memory_usage_uss_pss
- sum_matches(PROC_SMAPS_ROLLUP_PATH, uss: PRIVATE_PAGES_PATTERN, pss: PSS_PATTERN)
- .transform_values(&:kilobytes)
- end
+ mem
+ end
- def self.file_descriptor_count
- Dir.glob(PROC_FD_GLOB).length
- end
+ def self.file_descriptor_count
+ Dir.glob('/proc/self/fd/*').length
+ end
+
+ def self.max_open_file_descriptors
+ match = File.read('/proc/self/limits').match(/Max open files\s*(\d+)/)
+
+ return unless match && match[1]
- def self.max_open_file_descriptors
- sum_matches(PROC_LIMITS_PATH, max_fds: MAX_OPEN_FILES_PATTERN)[:max_fds]
+ match[1].to_i
+ end
+ else
+ def self.memory_usage
+ 0.0
+ end
+
+ def self.file_descriptor_count
+ 0
+ end
+
+ def self.max_open_file_descriptors
+ 0
+ end
end
def self.cpu_time
- Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :float_second)
+ Process
+ .clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :float_second)
end
# Returns the current real time in a given precision.
@@ -68,27 +78,6 @@ module Gitlab
end_time - start_time
end
-
- # Given a path to a file in /proc and a hash of (metric, pattern) pairs,
- # sums up all values found for those patterns under the respective metric.
- def self.sum_matches(proc_file, **patterns)
- results = patterns.transform_values { 0 }
-
- begin
- File.foreach(proc_file) do |line|
- patterns.each do |metric, pattern|
- match = line.match(pattern)
- value = match&.named_captures&.fetch('value', 0)
- results[metric] += value.to_i
- end
- end
- rescue Errno::ENOENT
- # This means the procfile we're reading from did not exist;
- # this is safe to ignore, since we initialize each metric to 0
- end
-
- results
- end
end
end
end
diff --git a/lib/gitlab/user_access_snippet.rb b/lib/gitlab/user_access_snippet.rb
index bfed86c4df4..dcd45f9350d 100644
--- a/lib/gitlab/user_access_snippet.rb
+++ b/lib/gitlab/user_access_snippet.rb
@@ -17,7 +17,14 @@ module Gitlab
@project = snippet&.project
end
+ def allowed?
+ return true if snippet_migration?
+
+ super
+ end
+
def can_do_action?(action)
+ return true if snippet_migration?
return false unless can_access_git?
permission_cache[action] =
@@ -35,7 +42,10 @@ module Gitlab
end
def can_push_to_branch?(ref)
+ return true if snippet_migration?
+
super
+
return false unless snippet
return false unless can_do_action?(:update_snippet)
@@ -45,5 +55,9 @@ module Gitlab
def can_merge_to_branch?(ref)
false
end
+
+ def snippet_migration?
+ user&.migration_bot? && snippet
+ end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 1f29126109d..b77364cc74b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1356,7 +1356,7 @@ msgstr ""
msgid "Adds an issue to an epic."
msgstr ""
-msgid "Adjust your filters/search criteria above."
+msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
msgstr ""
msgid "Admin Area"
@@ -9790,6 +9790,9 @@ msgstr ""
msgid "Geo|%{name} is scheduled for re-verify"
msgstr ""
+msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|All"
msgstr ""
@@ -9913,6 +9916,9 @@ msgstr ""
msgid "Geo|The node is currently %{minutes_behind} behind the primary node."
msgstr ""
+msgid "Geo|There are no %{replicable_type} to show"
+msgstr ""
+
msgid "Geo|Tracking database entry will be removed. Are you sure?"
msgstr ""
@@ -11132,9 +11138,6 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
msgstr ""
-msgid "If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "If you lose your recovery codes you can generate new ones, invalidating all previous codes."
msgstr ""
@@ -13798,9 +13801,6 @@ msgstr ""
msgid "No %{providerTitle} repositories found"
msgstr ""
-msgid "No %{replicableType} match this filter"
-msgstr ""
-
msgid "No Epic"
msgstr ""
@@ -20962,6 +20962,9 @@ msgstr ""
msgid "The vulnerability is no longer detected. Verify the vulnerability has been remediated before changing its status."
msgstr ""
+msgid "There are no %{replicableType} to show"
+msgstr ""
+
msgid "There are no GPG keys associated with this account."
msgstr ""
@@ -25821,6 +25824,9 @@ msgstr ""
msgid "updated %{time_ago}"
msgstr ""
+msgid "uploads"
+msgstr ""
+
msgid "user avatar"
msgstr ""
diff --git a/package.json b/package.json
index 5b145853ac7..acac9f5f36b 100644
--- a/package.json
+++ b/package.json
@@ -44,7 +44,7 @@
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "^6.0.2-2",
"@sentry/browser": "^5.10.2",
- "@sourcegraph/code-host-integration": "0.0.37",
+ "@sourcegraph/code-host-integration": "0.0.46",
"@toast-ui/editor": "^2.0.1",
"@toast-ui/vue-editor": "^2.0.1",
"apollo-cache-inmemory": "^1.6.3",
diff --git a/spec/frontend/pipelines/header_component_spec.js b/spec/frontend/pipelines/header_component_spec.js
new file mode 100644
index 00000000000..1c3a6c545a0
--- /dev/null
+++ b/spec/frontend/pipelines/header_component_spec.js
@@ -0,0 +1,116 @@
+import { shallowMount } from '@vue/test-utils';
+import HeaderComponent from '~/pipelines/components/header_component.vue';
+import CiHeader from '~/vue_shared/components/header_ci_component.vue';
+import eventHub from '~/pipelines/event_hub';
+import { GlModal } from '@gitlab/ui';
+
+describe('Pipeline details header', () => {
+ let wrapper;
+ let glModalDirective;
+
+ const threeWeeksAgo = new Date();
+ threeWeeksAgo.setDate(threeWeeksAgo.getDate() - 21);
+
+ const findDeleteModal = () => wrapper.find(GlModal);
+
+ const defaultProps = {
+ pipeline: {
+ details: {
+ status: {
+ group: 'failed',
+ icon: 'status_failed',
+ label: 'failed',
+ text: 'failed',
+ details_path: 'path',
+ },
+ },
+ id: 123,
+ created_at: threeWeeksAgo.toISOString(),
+ user: {
+ web_url: 'path',
+ name: 'Foo',
+ username: 'foobar',
+ email: 'foo@bar.com',
+ avatar_url: 'link',
+ },
+ retry_path: 'retry',
+ cancel_path: 'cancel',
+ delete_path: 'delete',
+ },
+ isLoading: false,
+ };
+
+ const createComponent = (props = {}) => {
+ glModalDirective = jest.fn();
+
+ wrapper = shallowMount(HeaderComponent, {
+ propsData: {
+ ...props,
+ },
+ directives: {
+ glModal: {
+ bind(el, { value }) {
+ glModalDirective(value);
+ },
+ },
+ },
+ });
+ };
+
+ beforeEach(() => {
+ jest.spyOn(eventHub, '$emit');
+
+ createComponent(defaultProps);
+ });
+
+ afterEach(() => {
+ eventHub.$off();
+
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('should render provided pipeline info', () => {
+ expect(wrapper.find(CiHeader).props()).toMatchObject({
+ status: defaultProps.pipeline.details.status,
+ itemId: defaultProps.pipeline.id,
+ time: defaultProps.pipeline.created_at,
+ user: defaultProps.pipeline.user,
+ });
+ });
+
+ describe('action buttons', () => {
+ it('should not trigger eventHub when nothing happens', () => {
+ expect(eventHub.$emit).not.toHaveBeenCalled();
+ });
+
+ it('should call postAction when retry button action is clicked', () => {
+ wrapper.find('.js-retry-button').vm.$emit('click');
+
+ expect(eventHub.$emit).toHaveBeenCalledWith('headerPostAction', 'retry');
+ });
+
+ it('should call postAction when cancel button action is clicked', () => {
+ wrapper.find('.js-btn-cancel-pipeline').vm.$emit('click');
+
+ expect(eventHub.$emit).toHaveBeenCalledWith('headerPostAction', 'cancel');
+ });
+
+ it('does not show delete modal', () => {
+ expect(findDeleteModal()).not.toBeVisible();
+ });
+
+ describe('when delete button action is clicked', () => {
+ it('displays delete modal', () => {
+ expect(findDeleteModal().props('modalId')).toBe(wrapper.vm.$options.DELETE_MODAL_ID);
+ expect(glModalDirective).toHaveBeenCalledWith(wrapper.vm.$options.DELETE_MODAL_ID);
+ });
+
+ it('should call delete when modal is submitted', () => {
+ findDeleteModal().vm.$emit('ok');
+
+ expect(eventHub.$emit).toHaveBeenCalledWith('headerDeleteAction', 'delete');
+ });
+ });
+ });
+});
diff --git a/spec/frontend/pipelines/pipelines_actions_spec.js b/spec/frontend/pipelines/pipelines_actions_spec.js
new file mode 100644
index 00000000000..5e8d21660de
--- /dev/null
+++ b/spec/frontend/pipelines/pipelines_actions_spec.js
@@ -0,0 +1,142 @@
+import { shallowMount } from '@vue/test-utils';
+import MockAdapter from 'axios-mock-adapter';
+import { TEST_HOST } from 'spec/test_constants';
+import axios from '~/lib/utils/axios_utils';
+import PipelinesActions from '~/pipelines/components/pipelines_actions.vue';
+import { GlDeprecatedButton } from '@gitlab/ui';
+import GlCountdown from '~/vue_shared/components/gl_countdown.vue';
+import waitForPromises from 'helpers/wait_for_promises';
+
+describe('Pipelines Actions dropdown', () => {
+ let wrapper;
+ let mock;
+
+ const createComponent = (actions = []) => {
+ wrapper = shallowMount(PipelinesActions, {
+ propsData: {
+ actions,
+ },
+ });
+ };
+
+ const findAllDropdownItems = () => wrapper.findAll(GlDeprecatedButton);
+ const findAllCountdowns = () => wrapper.findAll(GlCountdown);
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+
+ mock.restore();
+ });
+
+ describe('manual actions', () => {
+ const mockActions = [
+ {
+ name: 'stop_review',
+ path: `${TEST_HOST}/root/review-app/builds/1893/play`,
+ },
+ {
+ name: 'foo',
+ path: `${TEST_HOST}/disabled/pipeline/action`,
+ playable: false,
+ },
+ ];
+
+ beforeEach(() => {
+ createComponent(mockActions);
+ });
+
+ it('renders a dropdown with the provided actions', () => {
+ expect(findAllDropdownItems()).toHaveLength(mockActions.length);
+ });
+
+ it("renders a disabled action when it's not playable", () => {
+ expect(
+ findAllDropdownItems()
+ .at(1)
+ .attributes('disabled'),
+ ).toBe('true');
+ });
+
+ describe('on click', () => {
+ it('makes a request and toggles the loading state', () => {
+ mock.onPost(mockActions.path).reply(200);
+
+ wrapper.find(GlDeprecatedButton).vm.$emit('click');
+
+ expect(wrapper.vm.isLoading).toBe(true);
+
+ return waitForPromises().then(() => {
+ expect(wrapper.vm.isLoading).toBe(false);
+ });
+ });
+ });
+ });
+
+ describe('scheduled jobs', () => {
+ const scheduledJobAction = {
+ name: 'scheduled action',
+ path: `${TEST_HOST}/scheduled/job/action`,
+ playable: true,
+ scheduled_at: '2063-04-05T00:42:00Z',
+ };
+ const expiredJobAction = {
+ name: 'expired action',
+ path: `${TEST_HOST}/expired/job/action`,
+ playable: true,
+ scheduled_at: '2018-10-05T08:23:00Z',
+ };
+
+ beforeEach(() => {
+ jest.spyOn(Date, 'now').mockImplementation(() => new Date('2063-04-04T00:42:00Z').getTime());
+ createComponent([scheduledJobAction, expiredJobAction]);
+ });
+
+ it('makes post request after confirming', () => {
+ mock.onPost(scheduledJobAction.path).reply(200);
+ jest.spyOn(window, 'confirm').mockReturnValue(true);
+
+ findAllDropdownItems()
+ .at(0)
+ .vm.$emit('click');
+
+ expect(window.confirm).toHaveBeenCalled();
+
+ return waitForPromises().then(() => {
+ expect(mock.history.post.length).toBe(1);
+ });
+ });
+
+ it('does not make post request if confirmation is cancelled', () => {
+ mock.onPost(scheduledJobAction.path).reply(200);
+ jest.spyOn(window, 'confirm').mockReturnValue(false);
+
+ findAllDropdownItems()
+ .at(0)
+ .vm.$emit('click');
+
+ expect(window.confirm).toHaveBeenCalled();
+ expect(mock.history.post.length).toBe(0);
+ });
+
+ it('displays the remaining time in the dropdown', () => {
+ expect(
+ findAllCountdowns()
+ .at(0)
+ .props('endDateString'),
+ ).toBe(scheduledJobAction.scheduled_at);
+ });
+
+ it('displays 00:00:00 for expired jobs in the dropdown', () => {
+ expect(
+ findAllCountdowns()
+ .at(1)
+ .props('endDateString'),
+ ).toBe(expiredJobAction.scheduled_at);
+ });
+ });
+});
diff --git a/spec/javascripts/pipelines/header_component_spec.js b/spec/javascripts/pipelines/header_component_spec.js
deleted file mode 100644
index 9043f30397d..00000000000
--- a/spec/javascripts/pipelines/header_component_spec.js
+++ /dev/null
@@ -1,108 +0,0 @@
-import Vue from 'vue';
-import headerComponent from '~/pipelines/components/header_component.vue';
-import eventHub from '~/pipelines/event_hub';
-
-describe('Pipeline details header', () => {
- let HeaderComponent;
- let vm;
- let props;
-
- beforeEach(() => {
- spyOn(eventHub, '$emit');
- HeaderComponent = Vue.extend(headerComponent);
-
- const threeWeeksAgo = new Date();
- threeWeeksAgo.setDate(threeWeeksAgo.getDate() - 21);
-
- props = {
- pipeline: {
- details: {
- status: {
- group: 'failed',
- icon: 'status_failed',
- label: 'failed',
- text: 'failed',
- details_path: 'path',
- },
- },
- id: 123,
- created_at: threeWeeksAgo.toISOString(),
- user: {
- web_url: 'path',
- name: 'Foo',
- username: 'foobar',
- email: 'foo@bar.com',
- avatar_url: 'link',
- },
- retry_path: 'retry',
- cancel_path: 'cancel',
- delete_path: 'delete',
- },
- isLoading: false,
- };
-
- vm = new HeaderComponent({ propsData: props }).$mount();
- });
-
- afterEach(() => {
- eventHub.$off();
- vm.$destroy();
- });
-
- const findDeleteModal = () => document.getElementById(headerComponent.DELETE_MODAL_ID);
- const findDeleteModalSubmit = () =>
- [...findDeleteModal().querySelectorAll('.btn')].find(x => x.textContent === 'Delete pipeline');
-
- it('should render provided pipeline info', () => {
- expect(
- vm.$el
- .querySelector('.header-main-content')
- .textContent.replace(/\s+/g, ' ')
- .trim(),
- ).toContain('failed Pipeline #123 triggered 3 weeks ago by Foo');
- });
-
- describe('action buttons', () => {
- it('should not trigger eventHub when nothing happens', () => {
- expect(eventHub.$emit).not.toHaveBeenCalled();
- });
-
- it('should call postAction when retry button action is clicked', () => {
- vm.$el.querySelector('.js-retry-button').click();
-
- expect(eventHub.$emit).toHaveBeenCalledWith('headerPostAction', 'retry');
- });
-
- it('should call postAction when cancel button action is clicked', () => {
- vm.$el.querySelector('.js-btn-cancel-pipeline').click();
-
- expect(eventHub.$emit).toHaveBeenCalledWith('headerPostAction', 'cancel');
- });
-
- it('does not show delete modal', () => {
- expect(findDeleteModal()).not.toBeVisible();
- });
-
- describe('when delete button action is clicked', () => {
- beforeEach(done => {
- vm.$el.querySelector('.js-btn-delete-pipeline').click();
-
- // Modal needs two ticks to show
- vm.$nextTick()
- .then(() => vm.$nextTick())
- .then(done)
- .catch(done.fail);
- });
-
- it('should show delete modal', () => {
- expect(findDeleteModal()).toBeVisible();
- });
-
- it('should call delete when modal is submitted', () => {
- findDeleteModalSubmit().click();
-
- expect(eventHub.$emit).toHaveBeenCalledWith('headerDeleteAction', 'delete');
- });
- });
- });
-});
diff --git a/spec/javascripts/pipelines/pipelines_actions_spec.js b/spec/javascripts/pipelines/pipelines_actions_spec.js
deleted file mode 100644
index 91f7d2167cc..00000000000
--- a/spec/javascripts/pipelines/pipelines_actions_spec.js
+++ /dev/null
@@ -1,128 +0,0 @@
-import Vue from 'vue';
-import MockAdapter from 'axios-mock-adapter';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-import { TEST_HOST } from 'spec/test_constants';
-import axios from '~/lib/utils/axios_utils';
-import PipelinesActions from '~/pipelines/components/pipelines_actions.vue';
-
-describe('Pipelines Actions dropdown', () => {
- const Component = Vue.extend(PipelinesActions);
- let vm;
- let mock;
-
- afterEach(() => {
- vm.$destroy();
- mock.restore();
- });
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- describe('manual actions', () => {
- const actions = [
- {
- name: 'stop_review',
- path: `${TEST_HOST}/root/review-app/builds/1893/play`,
- },
- {
- name: 'foo',
- path: `${TEST_HOST}/disabled/pipeline/action`,
- playable: false,
- },
- ];
-
- beforeEach(() => {
- vm = mountComponent(Component, { actions });
- });
-
- it('renders a dropdown with the provided actions', () => {
- const dropdownItems = vm.$el.querySelectorAll('.dropdown-menu li');
-
- expect(dropdownItems.length).toEqual(actions.length);
- });
-
- it("renders a disabled action when it's not playable", () => {
- const dropdownItem = vm.$el.querySelector('.dropdown-menu li:last-child button');
-
- expect(dropdownItem).toBeDisabled();
- });
-
- describe('on click', () => {
- it('makes a request and toggles the loading state', done => {
- mock.onPost(actions.path).reply(200);
-
- vm.$el.querySelector('.dropdown-menu li button').click();
-
- expect(vm.isLoading).toEqual(true);
-
- setTimeout(() => {
- expect(vm.isLoading).toEqual(false);
-
- done();
- });
- });
- });
- });
-
- describe('scheduled jobs', () => {
- const scheduledJobAction = {
- name: 'scheduled action',
- path: `${TEST_HOST}/scheduled/job/action`,
- playable: true,
- scheduled_at: '2063-04-05T00:42:00Z',
- };
- const expiredJobAction = {
- name: 'expired action',
- path: `${TEST_HOST}/expired/job/action`,
- playable: true,
- scheduled_at: '2018-10-05T08:23:00Z',
- };
- const findDropdownItem = action => {
- const buttons = vm.$el.querySelectorAll('.dropdown-menu li button');
- return Array.prototype.find.call(buttons, element =>
- element.innerText.trim().startsWith(action.name),
- );
- };
-
- beforeEach(done => {
- spyOn(Date, 'now').and.callFake(() => new Date('2063-04-04T00:42:00Z').getTime());
- vm = mountComponent(Component, { actions: [scheduledJobAction, expiredJobAction] });
-
- Vue.nextTick()
- .then(done)
- .catch(done.fail);
- });
-
- it('makes post request after confirming', done => {
- mock.onPost(scheduledJobAction.path).reply(200);
- spyOn(window, 'confirm').and.callFake(() => true);
-
- findDropdownItem(scheduledJobAction).click();
-
- expect(window.confirm).toHaveBeenCalled();
- setTimeout(() => {
- expect(mock.history.post.length).toBe(1);
- done();
- });
- });
-
- it('does not make post request if confirmation is cancelled', () => {
- mock.onPost(scheduledJobAction.path).reply(200);
- spyOn(window, 'confirm').and.callFake(() => false);
-
- findDropdownItem(scheduledJobAction).click();
-
- expect(window.confirm).toHaveBeenCalled();
- expect(mock.history.post.length).toBe(0);
- });
-
- it('displays the remaining time in the dropdown', () => {
- expect(findDropdownItem(scheduledJobAction)).toContainText('24:00:00');
- });
-
- it('displays 00:00:00 for expired jobs in the dropdown', () => {
- expect(findDropdownItem(expiredJobAction)).toContainText('00:00:00');
- });
- });
-});
diff --git a/spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb b/spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb
index 94ef094e72c..dcbf8d12f35 100644
--- a/spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb
@@ -25,7 +25,7 @@ describe Gitlab::BackgroundMigration::BackfillSnippetRepositories, :migration, s
confirmed_at: 1.day.ago)
end
- let!(:admin) { users.create(id: 2, email: 'admin@example.com', projects_limit: 10, username: 'admin', name: 'Admin', admin: true, state: 'active') }
+ let(:migration_bot) { User.migration_bot }
let!(:snippet_with_repo) { snippets.create(id: 1, type: 'PersonalSnippet', author_id: user.id, file_name: file_name, content: content) }
let!(:snippet_with_empty_repo) { snippets.create(id: 2, type: 'PersonalSnippet', author_id: user.id, file_name: file_name, content: content) }
let!(:snippet_without_repo) { snippets.create(id: 3, type: 'PersonalSnippet', author_id: user.id, file_name: file_name, content: content) }
@@ -88,34 +88,34 @@ describe Gitlab::BackgroundMigration::BackfillSnippetRepositories, :migration, s
end
context 'when author cannot update snippet or use git' do
- shared_examples 'admin user commits files' do
+ shared_examples 'migration_bot user commits files' do
it do
subject
last_commit = raw_repository(snippet).commit
- expect(last_commit.author_name).to eq admin.name
- expect(last_commit.author_email).to eq admin.email
+ expect(last_commit.author_name).to eq migration_bot.name
+ expect(last_commit.author_email).to eq migration_bot.email
end
end
context 'when user is blocked' do
let(:user_state) { 'blocked' }
- it_behaves_like 'admin user commits files'
+ it_behaves_like 'migration_bot user commits files'
end
context 'when user is deactivated' do
let(:user_state) { 'deactivated' }
- it_behaves_like 'admin user commits files'
+ it_behaves_like 'migration_bot user commits files'
end
context 'when user is a ghost' do
let(:ghost) { true }
let(:user_type) { 'ghost' }
- it_behaves_like 'admin user commits files'
+ it_behaves_like 'migration_bot user commits files'
end
end
end
diff --git a/spec/lib/gitlab/git_access_snippet_spec.rb b/spec/lib/gitlab/git_access_snippet_spec.rb
index 1c369d57e06..fb2a7d16665 100644
--- a/spec/lib/gitlab/git_access_snippet_spec.rb
+++ b/spec/lib/gitlab/git_access_snippet_spec.rb
@@ -11,8 +11,9 @@ describe Gitlab::GitAccessSnippet do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public) }
let_it_be(:snippet) { create(:project_snippet, :public, :repository, project: project) }
- let(:repository) { snippet.repository }
+ let_it_be(:migration_bot) { User.migration_bot }
+ let(:repository) { snippet.repository }
let(:actor) { user }
let(:protocol) { 'ssh' }
let(:changes) { Gitlab::GitAccess::ANY }
@@ -27,10 +28,22 @@ describe Gitlab::GitAccessSnippet do
let(:actor) { build(:deploy_key) }
it 'does not allow push and pull access' do
+ expect { push_access_check }.to raise_forbidden(described_class::ERROR_MESSAGES[:authentication_mechanism])
expect { pull_access_check }.to raise_forbidden(described_class::ERROR_MESSAGES[:authentication_mechanism])
end
end
+ shared_examples 'actor is migration bot' do
+ context 'when user is the migration bot' do
+ let(:user) { migration_bot }
+
+ it 'can perform git operations' do
+ expect { push_access_check }.not_to raise_error
+ expect { pull_access_check }.not_to raise_error
+ end
+ end
+ end
+
describe '#check_snippet_accessibility!' do
context 'when the snippet exists' do
it 'allows access' do
@@ -77,6 +90,12 @@ describe Gitlab::GitAccessSnippet do
expect { push_access_check }.not_to raise_error
expect { pull_access_check }.not_to raise_error
end
+
+ it_behaves_like 'actor is migration bot' do
+ before do
+ expect(migration_bot.required_terms_not_accepted?).to be_truthy
+ end
+ end
end
context 'project snippet accessibility', :aggregate_failures do
@@ -107,6 +126,7 @@ describe Gitlab::GitAccessSnippet do
context 'when project is public' do
it_behaves_like 'checks accessibility'
+ it_behaves_like 'actor is migration bot'
end
context 'when project is public but snippet feature is private' do
@@ -117,6 +137,7 @@ describe Gitlab::GitAccessSnippet do
end
it_behaves_like 'checks accessibility'
+ it_behaves_like 'actor is migration bot'
end
context 'when project is not accessible' do
@@ -127,11 +148,58 @@ describe Gitlab::GitAccessSnippet do
let(:membership) { membership }
it 'respects accessibility' do
- expect { push_access_check }.to raise_error(described_class::NotFoundError)
- expect { pull_access_check }.to raise_error(described_class::NotFoundError)
+ expect { push_access_check }.to raise_snippet_not_found
+ expect { pull_access_check }.to raise_snippet_not_found
+ end
+ end
+ end
+
+ it_behaves_like 'actor is migration bot'
+ end
+
+ context 'when project is archived' do
+ let(:project) { create(:project, :public, :archived) }
+
+ [:anonymous, :non_member].each do |membership|
+ context membership.to_s do
+ let(:membership) { membership }
+
+ it 'cannot perform git operations' do
+ expect { push_access_check }.to raise_error(described_class::ForbiddenError)
+ expect { pull_access_check }.to raise_error(described_class::ForbiddenError)
+ end
+ end
+ end
+
+ [:guest, :reporter, :maintainer, :author, :admin].each do |membership|
+ context membership.to_s do
+ let(:membership) { membership }
+
+ it 'cannot perform git pushes' do
+ expect { push_access_check }.to raise_error(described_class::ForbiddenError)
+ expect { pull_access_check }.not_to raise_error
+ end
+ end
+ end
+
+ it_behaves_like 'actor is migration bot'
+ end
+
+ context 'when snippet feature is disabled' do
+ let(:project) { create(:project, :public, :snippets_disabled) }
+
+ [:anonymous, :non_member, :author, :admin].each do |membership|
+ context membership.to_s do
+ let(:membership) { membership }
+
+ it 'cannot perform git operations' do
+ expect { push_access_check }.to raise_error(described_class::ForbiddenError)
+ expect { pull_access_check }.to raise_error(described_class::ForbiddenError)
end
end
end
+
+ it_behaves_like 'actor is migration bot'
end
end
@@ -159,6 +227,8 @@ describe Gitlab::GitAccessSnippet do
expect { pull_access_check }.to raise_error(error_class)
end
end
+
+ it_behaves_like 'actor is migration bot'
end
end
@@ -166,36 +236,56 @@ describe Gitlab::GitAccessSnippet do
let(:user) { snippet.author }
let!(:primary_node) { FactoryBot.create(:geo_node, :primary) }
- # Without override, push access would return Gitlab::GitAccessResult::CustomAction
- it 'skips geo for snippet' do
+ before do
allow(::Gitlab::Database).to receive(:read_only?).and_return(true)
allow(::Gitlab::Geo).to receive(:secondary_with_primary?).and_return(true)
+ end
+ # Without override, push access would return Gitlab::GitAccessResult::CustomAction
+ it 'skips geo for snippet' do
expect { push_access_check }.to raise_forbidden(/You can't push code to a read-only GitLab instance/)
end
+
+ context 'when user is migration bot' do
+ let(:user) { migration_bot }
+
+ it 'skips geo for snippet' do
+ expect { push_access_check }.to raise_forbidden(/You can't push code to a read-only GitLab instance/)
+ end
+ end
end
context 'when changes are specific' do
let(:changes) { "2d1db523e11e777e49377cfb22d368deec3f0793 ddd0f15ae83993f5cb66a927a28673882e99100b master" }
let(:user) { snippet.author }
- it 'does not raise error if SnippetCheck does not raise error' do
- expect_next_instance_of(Gitlab::Checks::SnippetCheck) do |check|
- expect(check).to receive(:validate!).and_call_original
- end
- expect_next_instance_of(Gitlab::Checks::PushFileCountCheck) do |check|
- expect(check).to receive(:validate!)
+ shared_examples 'snippet checks' do
+ it 'does not raise error if SnippetCheck does not raise error' do
+ expect_next_instance_of(Gitlab::Checks::SnippetCheck) do |check|
+ expect(check).to receive(:validate!).and_call_original
+ end
+ expect_next_instance_of(Gitlab::Checks::PushFileCountCheck) do |check|
+ expect(check).to receive(:validate!)
+ end
+
+ expect { push_access_check }.not_to raise_error
end
- expect { push_access_check }.not_to raise_error
- end
+ it 'raises error if SnippetCheck raises error' do
+ expect_next_instance_of(Gitlab::Checks::SnippetCheck) do |check|
+ allow(check).to receive(:validate!).and_raise(Gitlab::GitAccess::ForbiddenError, 'foo')
+ end
- it 'raises error if SnippetCheck raises error' do
- expect_next_instance_of(Gitlab::Checks::SnippetCheck) do |check|
- allow(check).to receive(:validate!).and_raise(Gitlab::GitAccess::ForbiddenError, 'foo')
+ expect { push_access_check }.to raise_forbidden('foo')
end
+ end
- expect { push_access_check }.to raise_forbidden('foo')
+ it_behaves_like 'snippet checks'
+
+ context 'when user is migration bot' do
+ let(:user) { migration_bot }
+
+ it_behaves_like 'snippet checks'
end
end
@@ -260,6 +350,14 @@ describe Gitlab::GitAccessSnippet do
it_behaves_like 'a push to repository already over the limit'
it_behaves_like 'a push to repository below the limit'
it_behaves_like 'a push to repository to make it over the limit'
+
+ context 'when user is migration bot' do
+ let(:actor) { migration_bot }
+
+ it_behaves_like 'a push to repository already over the limit'
+ it_behaves_like 'a push to repository below the limit'
+ it_behaves_like 'a push to repository to make it over the limit'
+ end
end
context 'when GIT_OBJECT_DIRECTORY_RELATIVE env var is not set' do
@@ -274,6 +372,14 @@ describe Gitlab::GitAccessSnippet do
it_behaves_like 'a push to repository already over the limit'
it_behaves_like 'a push to repository below the limit'
it_behaves_like 'a push to repository to make it over the limit'
+
+ context 'when user is migration bot' do
+ let(:actor) { migration_bot }
+
+ it_behaves_like 'a push to repository already over the limit'
+ it_behaves_like 'a push to repository below the limit'
+ it_behaves_like 'a push to repository to make it over the limit'
+ end
end
end
diff --git a/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb b/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb
index 9d8ec2d9b21..8c4071a7ed1 100644
--- a/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb
+++ b/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb
@@ -19,19 +19,20 @@ describe Gitlab::Metrics::Samplers::RubySampler do
end
describe '#sample' do
- it 'adds a metric containing the process resident memory bytes' do
- expect(Gitlab::Metrics::System).to receive(:memory_usage).and_return(9000)
-
- expect(sampler.metrics[:process_resident_memory_bytes]).to receive(:set).with({}, 9000)
+ it 'samples various statistics' do
+ expect(Gitlab::Metrics::System).to receive(:cpu_time)
+ expect(Gitlab::Metrics::System).to receive(:file_descriptor_count)
+ expect(Gitlab::Metrics::System).to receive(:memory_usage)
+ expect(Gitlab::Metrics::System).to receive(:max_open_file_descriptors)
+ expect(sampler).to receive(:sample_gc)
sampler.sample
end
- it 'adds a metric containing the process unique and proportional memory bytes' do
- expect(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).and_return(uss: 9000, pss: 10_000)
+ it 'adds a metric containing the process resident memory bytes' do
+ expect(Gitlab::Metrics::System).to receive(:memory_usage).and_return(9000)
- expect(sampler.metrics[:process_unique_memory_bytes]).to receive(:set).with({}, 9000)
- expect(sampler.metrics[:process_proportional_memory_bytes]).to receive(:set).with({}, 10_000)
+ expect(sampler.metrics[:process_resident_memory_bytes]).to receive(:set).with({}, 9000)
sampler.sample
end
diff --git a/spec/lib/gitlab/metrics/system_spec.rb b/spec/lib/gitlab/metrics/system_spec.rb
index 37d26bd9d63..a5aa80686fd 100644
--- a/spec/lib/gitlab/metrics/system_spec.rb
+++ b/spec/lib/gitlab/metrics/system_spec.rb
@@ -3,122 +3,33 @@
require 'spec_helper'
describe Gitlab::Metrics::System do
- context 'when /proc files exist' do
- # Fixtures pulled from:
- # Linux carbon 5.3.0-7648-generic #41~1586789791~19.10~9593806-Ubuntu SMP Mon Apr 13 17:50:40 UTC x86_64 x86_64 x86_64 GNU/Linux
- let(:proc_status) do
- # most rows omitted for brevity
- <<~SNIP
- Name: less
- VmHWM: 2468 kB
- VmRSS: 2468 kB
- RssAnon: 260 kB
- SNIP
- end
-
- let(:proc_smaps_rollup) do
- # full snapshot
- <<~SNIP
- Rss: 2564 kB
- Pss: 503 kB
- Pss_Anon: 312 kB
- Pss_File: 191 kB
- Pss_Shmem: 0 kB
- Shared_Clean: 2100 kB
- Shared_Dirty: 0 kB
- Private_Clean: 152 kB
- Private_Dirty: 312 kB
- Referenced: 2564 kB
- Anonymous: 312 kB
- LazyFree: 0 kB
- AnonHugePages: 0 kB
- ShmemPmdMapped: 0 kB
- Shared_Hugetlb: 0 kB
- Private_Hugetlb: 0 kB
- Swap: 0 kB
- SwapPss: 0 kB
- Locked: 0 kB
- SNIP
- end
-
- let(:proc_limits) do
- # full snapshot
- <<~SNIP
- Limit Soft Limit Hard Limit Units
- Max cpu time unlimited unlimited seconds
- Max file size unlimited unlimited bytes
- Max data size unlimited unlimited bytes
- Max stack size 8388608 unlimited bytes
- Max core file size 0 unlimited bytes
- Max resident set unlimited unlimited bytes
- Max processes 126519 126519 processes
- Max open files 1024 1048576 files
- Max locked memory 67108864 67108864 bytes
- Max address space unlimited unlimited bytes
- Max file locks unlimited unlimited locks
- Max pending signals 126519 126519 signals
- Max msgqueue size 819200 819200 bytes
- Max nice priority 0 0
- Max realtime priority 0 0
- Max realtime timeout unlimited unlimited us
- SNIP
- end
-
+ if File.exist?('/proc')
describe '.memory_usage' do
- it "returns the process' resident set size (RSS) in bytes" do
- mock_existing_proc_file('/proc/self/status', proc_status)
-
- expect(described_class.memory_usage).to eq(2527232)
+ it "returns the process' memory usage in bytes" do
+ expect(described_class.memory_usage).to be > 0
end
end
describe '.file_descriptor_count' do
it 'returns the amount of open file descriptors' do
- expect(Dir).to receive(:glob).and_return(['/some/path', '/some/other/path'])
-
- expect(described_class.file_descriptor_count).to eq(2)
+ expect(described_class.file_descriptor_count).to be > 0
end
end
describe '.max_open_file_descriptors' do
it 'returns the max allowed open file descriptors' do
- mock_existing_proc_file('/proc/self/limits', proc_limits)
-
- expect(described_class.max_open_file_descriptors).to eq(1024)
- end
- end
-
- describe '.memory_usage_uss_pss' do
- it "returns the process' unique and porportional set size (USS/PSS) in bytes" do
- mock_existing_proc_file('/proc/self/smaps_rollup', proc_smaps_rollup)
-
- # (Private_Clean (152 kB) + Private_Dirty (312 kB) + Private_Hugetlb (0 kB)) * 1024
- expect(described_class.memory_usage_uss_pss).to eq(uss: 475136, pss: 515072)
+ expect(described_class.max_open_file_descriptors).to be > 0
end
end
- end
-
- context 'when /proc files do not exist' do
- before do
- mock_missing_proc_file
- end
-
+ else
describe '.memory_usage' do
- it 'returns 0' do
- expect(described_class.memory_usage).to eq(0)
- end
- end
-
- describe '.memory_usage_uss_pss' do
- it "returns 0 for all components" do
- expect(described_class.memory_usage_uss_pss).to eq(uss: 0, pss: 0)
+ it 'returns 0.0' do
+ expect(described_class.memory_usage).to eq(0.0)
end
end
describe '.file_descriptor_count' do
it 'returns 0' do
- expect(Dir).to receive(:glob).and_return([])
-
expect(described_class.file_descriptor_count).to eq(0)
end
end
@@ -187,12 +98,4 @@ describe Gitlab::Metrics::System do
expect(described_class.thread_cpu_duration(start_time)).to be_nil
end
end
-
- def mock_existing_proc_file(path, content)
- allow(File).to receive(:foreach).with(path) { |_path, &block| content.each_line(&block) }
- end
-
- def mock_missing_proc_file
- allow(File).to receive(:foreach).and_raise(Errno::ENOENT)
- end
end
diff --git a/spec/lib/gitlab/user_access_snippet_spec.rb b/spec/lib/gitlab/user_access_snippet_spec.rb
index 57e52e2e93d..2e8a0a49a76 100644
--- a/spec/lib/gitlab/user_access_snippet_spec.rb
+++ b/spec/lib/gitlab/user_access_snippet_spec.rb
@@ -7,6 +7,8 @@ describe Gitlab::UserAccessSnippet do
let_it_be(:project) { create(:project, :private) }
let_it_be(:snippet) { create(:project_snippet, :private, project: project) }
+ let_it_be(:migration_bot) { User.migration_bot }
+
let(:user) { create(:user) }
describe '#can_do_action?' do
@@ -36,6 +38,14 @@ describe Gitlab::UserAccessSnippet do
expect(access.can_do_action?(:ability)).to eq(false)
end
end
+
+ context 'when user is migration bot' do
+ let(:user) { migration_bot }
+
+ it 'allows access' do
+ expect(access.can_do_action?(:ability)).to eq(true)
+ end
+ end
end
describe '#can_push_to_branch?' do
@@ -65,6 +75,16 @@ describe Gitlab::UserAccessSnippet do
end
end
+ context 'when user is migration bot' do
+ let(:user) { migration_bot }
+
+ it 'allows access' do
+ allow(Ability).to receive(:allowed?).and_return(false)
+
+ expect(access.can_push_to_branch?('random_branch')).to eq(true)
+ end
+ end
+
context 'when snippet is nil' do
let(:user) { create_user_from_membership(project, :admin) }
let(:snippet) { nil }
@@ -72,6 +92,14 @@ describe Gitlab::UserAccessSnippet do
it 'disallows access' do
expect(access.can_push_to_branch?('random_branch')).to eq(false)
end
+
+ context 'when user is migration bot' do
+ let(:user) { migration_bot }
+
+ it 'disallows access' do
+ expect(access.can_push_to_branch?('random_branch')).to eq(false)
+ end
+ end
end
end
@@ -79,17 +107,41 @@ describe Gitlab::UserAccessSnippet do
it 'returns false' do
expect(access.can_create_tag?('random_tag')).to be_falsey
end
+
+ context 'when user is migration bot' do
+ let(:user) { migration_bot }
+
+ it 'returns false' do
+ expect(access.can_create_tag?('random_tag')).to be_falsey
+ end
+ end
end
describe '#can_delete_branch?' do
it 'returns false' do
expect(access.can_delete_branch?('random_branch')).to be_falsey
end
+
+ context 'when user is migration bot' do
+ let(:user) { migration_bot }
+
+ it 'returns false' do
+ expect(access.can_delete_branch?('random_branch')).to be_falsey
+ end
+ end
end
describe '#can_merge_to_branch?' do
it 'returns false' do
expect(access.can_merge_to_branch?('random_branch')).to be_falsey
end
+
+ context 'when user is migration bot' do
+ let(:user) { migration_bot }
+
+ it 'returns false' do
+ expect(access.can_merge_to_branch?('random_branch')).to be_falsey
+ end
+ end
end
end
diff --git a/spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb b/spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb
index c4af829a5e2..8fe3f27c8b1 100644
--- a/spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb
+++ b/spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb
@@ -4,18 +4,27 @@ require 'spec_helper'
describe UpdateHeadPipelineForMergeRequestWorker do
describe '#perform' do
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
- let(:merge_request) { create(:merge_request, source_project: project) }
- let(:latest_sha) { 'b83d6e391c22777fca1ed3012fce84f633d7fed0' }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:merge_request) { create(:merge_request, source_project: project) }
+ let_it_be(:latest_sha) { 'b83d6e391c22777fca1ed3012fce84f633d7fed0' }
context 'when pipeline exists for the source project and branch' do
- before do
- create(:ci_empty_pipeline, project: project, ref: merge_request.source_branch, sha: latest_sha)
- end
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, project: project, ref: merge_request.source_branch, sha: latest_sha) }
it 'updates the head_pipeline_id of the merge_request' do
- expect { subject.perform(merge_request.id) }.to change { merge_request.reload.head_pipeline_id }
+ expect { subject.perform(merge_request.id) }
+ .to change { merge_request.reload.head_pipeline_id }.from(nil).to(pipeline.id)
+ end
+
+ it_behaves_like 'an idempotent worker' do
+ let(:job_args) { merge_request.id }
+
+ it 'sets the pipeline as the head pipeline when run multiple times' do
+ subject
+
+ expect(merge_request.reload.head_pipeline_id).to eq(pipeline.id)
+ end
end
context 'when merge request sha does not equal pipeline sha' do
@@ -27,6 +36,15 @@ describe UpdateHeadPipelineForMergeRequestWorker do
expect { subject.perform(merge_request.id) }
.not_to change { merge_request.reload.head_pipeline_id }
end
+
+ it_behaves_like 'an idempotent worker' do
+ let(:job_args) { merge_request.id }
+
+ it 'does not update the head_pipeline_id when run multiple times' do
+ expect { subject }
+ .not_to change { merge_request.reload.head_pipeline_id }
+ end
+ end
end
end
@@ -35,10 +53,19 @@ describe UpdateHeadPipelineForMergeRequestWorker do
expect { subject.perform(merge_request.id) }
.not_to change { merge_request.reload.head_pipeline_id }
end
+
+ it_behaves_like 'an idempotent worker' do
+ let(:job_args) { merge_request.id }
+
+ it 'does not update the head_pipeline_id when run multiple times' do
+ expect { subject }
+ .not_to change { merge_request.reload.head_pipeline_id }
+ end
+ end
end
context 'when a merge request pipeline exists' do
- let!(:merge_request_pipeline) do
+ let_it_be(:merge_request_pipeline) do
create(:ci_pipeline,
project: project,
source: :merge_request_event,
@@ -52,6 +79,16 @@ describe UpdateHeadPipelineForMergeRequestWorker do
.from(nil).to(merge_request_pipeline.id)
end
+ it_behaves_like 'an idempotent worker' do
+ let(:job_args) { merge_request.id }
+
+ it 'sets the merge request pipeline as the head pipeline when run multiple times' do
+ subject
+
+ expect(merge_request.reload.head_pipeline_id).to eq(merge_request_pipeline.id)
+ end
+ end
+
context 'when branch pipeline exists' do
let!(:branch_pipeline) do
create(:ci_pipeline, project: project, source: :push, sha: latest_sha)
@@ -62,6 +99,16 @@ describe UpdateHeadPipelineForMergeRequestWorker do
.to change { merge_request.reload.head_pipeline_id }
.from(nil).to(merge_request_pipeline.id)
end
+
+ it_behaves_like 'an idempotent worker' do
+ let(:job_args) { merge_request.id }
+
+ it 'sets the merge request pipeline as the head pipeline when run multiple times' do
+ subject
+
+ expect(merge_request.reload.head_pipeline_id).to eq(merge_request_pipeline.id)
+ end
+ end
end
end
end
diff --git a/yarn.lock b/yarn.lock
index bbbe8a2eb71..859fc145342 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1040,10 +1040,10 @@
"@sentry/types" "5.10.0"
tslib "^1.9.3"
-"@sourcegraph/code-host-integration@0.0.37":
- version "0.0.37"
- resolved "https://registry.yarnpkg.com/@sourcegraph/code-host-integration/-/code-host-integration-0.0.37.tgz#87f9a602e2a60520b6038311a67face2ece86827"
- integrity sha512-GQvNuPORLjsMhto57Ue1umeSV3cir+hMEaGxwCKmmq+cc9ZSZpuXa8RVBXuT5azN99K9/8zFps4woyPJ8wrjYA==
+"@sourcegraph/code-host-integration@0.0.46":
+ version "0.0.46"
+ resolved "https://registry.yarnpkg.com/@sourcegraph/code-host-integration/-/code-host-integration-0.0.46.tgz#05e4cda671ed00450be12461e6a3caff473675aa"
+ integrity sha512-ghzfaV5ydSWTamLPIDLl5tRvTtM2MBDRmQbWTPg9ZoCP/eHk61uCTAFEq02lsefDQHISZHldeClqRYhZvGDZfw==
"@toast-ui/editor@^2.0.1":
version "2.0.1"