summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-03-09 18:07:59 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-03-09 18:07:59 +0000
commit7ebcead8cfd2edb810dd0cbda816b6cfbd170fe3 (patch)
tree11880c4059c89149cf997e9b958fb6d32c7dbdad
parentf1a40d0db939dfe8ff95d385e652ff72566be765 (diff)
downloadgitlab-ce-7ebcead8cfd2edb810dd0cbda816b6cfbd170fe3.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/blob/file_template_mediator.js6
-rw-r--r--app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue43
-rw-r--r--app/assets/javascripts/blob/suggest_gitlab_ci_yml/index.js2
-rw-r--r--app/assets/javascripts/blob_edit/blob_bundle.js6
-rw-r--r--app/assets/javascripts/groups/components/group_item.vue6
-rw-r--r--app/assets/javascripts/groups/components/item_actions.vue2
-rw-r--r--app/assets/stylesheets/components/popover.scss6
-rw-r--r--app/services/clusters/applications/base_helm_service.rb2
-rw-r--r--app/views/projects/_commit_button.html.haml3
-rw-r--r--app/views/projects/blob/_editor.html.haml5
-rw-r--r--app/views/projects/blob/_suggest_gitlab_ci_yml.html.haml4
-rw-r--r--app/views/projects/blob/new.html.haml5
-rw-r--r--changelogs/unreleased/creator-pairing-group-list-padding.yml5
-rw-r--r--db/migrate/20200309105539_add_index_services_on_template.rb19
-rw-r--r--db/schema.rb2
-rw-r--r--doc/development/pipelines.md3
-rw-r--r--doc/development/testing_guide/end_to_end/index.md1
-rw-r--r--doc/development/testing_guide/end_to_end/rspec_metadata_tests.md13
-rw-r--r--doc/security/unlock_user.md2
-rw-r--r--doc/topics/autodevops/index.md9
-rw-r--r--lib/gitlab/kubernetes/helm/api.rb2
-rw-r--r--lib/gitlab/sidekiq_logging/deduplication_logger.rb19
-rw-r--r--lib/gitlab/sidekiq_logging/logs_jobs.rb25
-rw-r--r--lib/gitlab/sidekiq_logging/structured_logger.rb20
-rw-r--r--lib/gitlab/sidekiq_middleware/duplicate_jobs.rb13
-rw-r--r--lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb12
-rw-r--r--lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/until_executing.rb5
-rw-r--r--locale/gitlab.pot6
-rw-r--r--spec/frontend/blob/suggest_gitlab_ci_yml/components/popover_spec.js34
-rw-r--r--spec/frontend/diffs/components/tree_list_spec.js138
-rw-r--r--spec/javascripts/diffs/components/tree_list_spec.js126
-rw-r--r--spec/lib/gitlab/kubernetes/helm/api_spec.rb2
-rw-r--r--spec/lib/gitlab/sidekiq_logging/deduplication_logger_spec.rb33
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job_spec.rb32
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/until_executing_spec.rb34
-rw-r--r--spec/lib/gitlab/url_blocker_spec.rb66
-rw-r--r--spec/lib/gitlab/url_blockers/url_whitelist_spec.rb46
-rw-r--r--spec/services/clusters/applications/check_installation_progress_service_spec.rb2
-rw-r--r--spec/services/clusters/applications/check_uninstall_progress_service_spec.rb2
-rw-r--r--spec/services/clusters/applications/install_service_spec.rb2
-rw-r--r--spec/services/clusters/applications/patch_service_spec.rb2
-rw-r--r--spec/services/clusters/applications/uninstall_service_spec.rb2
-rw-r--r--spec/services/clusters/applications/upgrade_service_spec.rb2
43 files changed, 522 insertions, 247 deletions
diff --git a/app/assets/javascripts/blob/file_template_mediator.js b/app/assets/javascripts/blob/file_template_mediator.js
index 0fb02ca5965..d2c0ef330e4 100644
--- a/app/assets/javascripts/blob/file_template_mediator.js
+++ b/app/assets/javascripts/blob/file_template_mediator.js
@@ -9,6 +9,7 @@ import GitignoreSelector from './template_selectors/gitignore_selector';
import LicenseSelector from './template_selectors/license_selector';
import toast from '~/vue_shared/plugins/global_toast';
import { __ } from '~/locale';
+import initPopover from '~/blob/suggest_gitlab_ci_yml';
export default class FileTemplateMediator {
constructor({ editor, currentAction, projectId }) {
@@ -128,6 +129,7 @@ export default class FileTemplateMediator {
selectTemplateFile(selector, query, data) {
const self = this;
const { name } = selector.config;
+ const suggestCommitChanges = document.querySelector('.js-suggest-gitlab-ci-yml-commit-changes');
selector.renderLoading();
@@ -146,6 +148,10 @@ export default class FileTemplateMediator {
},
},
});
+
+ if (suggestCommitChanges) {
+ initPopover(suggestCommitChanges);
+ }
})
.catch(err => new Flash(`An error occurred while fetching the template: ${err}`));
}
diff --git a/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue b/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue
index fa3c19921df..d304ae7dbf6 100644
--- a/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue
+++ b/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue
@@ -1,10 +1,25 @@
<script>
import { GlPopover, GlSprintf, GlButton, GlIcon } from '@gitlab/ui';
import Cookies from 'js-cookie';
-import { parseBoolean } from '~/lib/utils/common_utils';
+import { parseBoolean, scrollToElement } from '~/lib/utils/common_utils';
import { s__ } from '~/locale';
import { glEmojiTag } from '~/emoji';
+const popoverStates = {
+ suggest_gitlab_ci_yml: {
+ title: s__(`suggestPipeline|1/2: Choose a template`),
+ content: s__(
+ `suggestPipeline|We recommend the %{boldStart}Code Quality%{boldEnd} template, which will add a report widget to your Merge Requests. This way you’ll learn about code quality degradations much sooner. %{footerStart} Goodbye technical debt! %{footerEnd}`,
+ ),
+ emoji: glEmojiTag('wave'),
+ },
+ suggest_commit_first_project_gitlab_ci_yml: {
+ title: s__(`suggestPipeline|2/2: Commit your changes`),
+ content: s__(
+ `suggestPipeline|Commit the changes and your pipeline will automatically run for the first time.`,
+ ),
+ },
+};
export default {
components: {
GlPopover,
@@ -17,7 +32,7 @@ export default {
type: String,
required: true,
},
- cssClass: {
+ trackLabel: {
type: String,
required: true,
},
@@ -33,17 +48,19 @@ export default {
},
computed: {
suggestTitle() {
- return s__(`suggestPipeline|1/2: Choose a template`);
+ return popoverStates[this.trackLabel].title || '';
},
suggestContent() {
- return s__(
- `suggestPipeline|We recommend the %{boldStart}Code Quality%{boldEnd} template, which will add a report widget to your Merge Requests. This way you’ll learn about code quality degradations much sooner. %{footerStart} Goodbye technical debt! %{footerEnd}`,
- );
+ return popoverStates[this.trackLabel].content || '';
},
emoji() {
- return glEmojiTag('wave');
+ return popoverStates[this.trackLabel].emoji || '';
},
},
+ mounted() {
+ if (this.trackLabel === 'suggest_commit_first_project_gitlab_ci_yml' && !this.popoverDismissed)
+ scrollToElement(document.querySelector(this.target));
+ },
methods: {
onDismiss() {
this.popoverDismissed = true;
@@ -61,13 +78,15 @@ export default {
placement="rightbottom"
trigger="manual"
container="viewport"
- :css-classes="[cssClass]"
+ :css-classes="['suggest-gitlab-ci-yml', 'ml-4']"
>
<template #title>
- <gl-button :aria-label="__('Close')" class="btn-blank float-right" @click="onDismiss">
- <gl-icon name="close" aria-hidden="true" />
- </gl-button>
- {{ suggestTitle }}
+ <span v-html="suggestTitle"></span>
+ <span class="ml-auto">
+ <gl-button :aria-label="__('Close')" class="btn-blank" @click="onDismiss">
+ <gl-icon name="close" aria-hidden="true" />
+ </gl-button>
+ </span>
</template>
<gl-sprintf :message="suggestContent">
diff --git a/app/assets/javascripts/blob/suggest_gitlab_ci_yml/index.js b/app/assets/javascripts/blob/suggest_gitlab_ci_yml/index.js
index f770000eb68..2cc342fb381 100644
--- a/app/assets/javascripts/blob/suggest_gitlab_ci_yml/index.js
+++ b/app/assets/javascripts/blob/suggest_gitlab_ci_yml/index.js
@@ -8,7 +8,7 @@ export default el =>
return createElement(Popover, {
props: {
target: el.dataset.target,
- cssClass: el.dataset.cssClass,
+ trackLabel: el.dataset.trackLabel,
dismissKey: el.dataset.dismissKey,
},
});
diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js
index 6aaf5bf7296..f4ce98037c8 100644
--- a/app/assets/javascripts/blob_edit/blob_bundle.js
+++ b/app/assets/javascripts/blob_edit/blob_bundle.js
@@ -4,11 +4,13 @@ import $ from 'jquery';
import NewCommitForm from '../new_commit_form';
import EditBlob from './edit_blob';
import BlobFileDropzone from '../blob/blob_file_dropzone';
+import initPopover from '~/blob/suggest_gitlab_ci_yml';
export default () => {
const editBlobForm = $('.js-edit-blob-form');
const uploadBlobForm = $('.js-upload-blob-form');
const deleteBlobForm = $('.js-delete-blob-form');
+ const suggestEl = document.querySelector('.js-suggest-gitlab-ci-yml');
if (editBlobForm.length) {
const urlRoot = editBlobForm.data('relativeUrlRoot');
@@ -56,4 +58,8 @@ export default () => {
if (deleteBlobForm.length) {
new NewCommitForm(deleteBlobForm);
}
+
+ if (suggestEl) {
+ initPopover(suggestEl);
+ }
};
diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue
index b192fb78631..1b8c75202fb 100644
--- a/app/assets/javascripts/groups/components/group_item.vue
+++ b/app/assets/javascripts/groups/components/group_item.vue
@@ -95,7 +95,7 @@ export default {
<li :id="groupDomId" :class="rowClass" class="group-row" @click.stop="onClickRowGroup">
<div
:class="{ 'project-row-contents': !isGroup }"
- class="group-row-contents d-flex align-items-center py-2"
+ class="group-row-contents d-flex align-items-center py-2 pr-3"
>
<div class="folder-toggle-wrap append-right-4 d-flex align-items-center">
<item-caret :is-group-open="group.isOpen" />
@@ -103,8 +103,8 @@ export default {
</div>
<gl-loading-icon
v-if="group.isChildrenLoading"
- size="md"
- class="d-none d-sm-inline-flex flex-shrink-0 append-right-10"
+ size="lg"
+ class="d-none d-sm-inline-flex flex-shrink-0 append-right-8"
/>
<div
:class="{ 'd-sm-flex': !group.isChildrenLoading }"
diff --git a/app/assets/javascripts/groups/components/item_actions.vue b/app/assets/javascripts/groups/components/item_actions.vue
index 4b569970204..5454480e61a 100644
--- a/app/assets/javascripts/groups/components/item_actions.vue
+++ b/app/assets/javascripts/groups/components/item_actions.vue
@@ -68,7 +68,7 @@ export default {
data-placement="bottom"
class="edit-group btn btn-xs no-expand"
>
- <icon name="settings" class="position-top-0" />
+ <icon name="settings" class="position-top-0 align-middle" />
</a>
</div>
</template>
diff --git a/app/assets/stylesheets/components/popover.scss b/app/assets/stylesheets/components/popover.scss
index 7e824c72f77..198500d07a7 100644
--- a/app/assets/stylesheets/components/popover.scss
+++ b/app/assets/stylesheets/components/popover.scss
@@ -144,9 +144,7 @@
.popover-header {
padding: $gl-padding;
-
- .ic-close {
- margin-top: -1em;
- }
+ display: flex;
+ align-items: center;
}
}
diff --git a/app/services/clusters/applications/base_helm_service.rb b/app/services/clusters/applications/base_helm_service.rb
index 57bc8bc0d9b..0c9b41be8d2 100644
--- a/app/services/clusters/applications/base_helm_service.rb
+++ b/app/services/clusters/applications/base_helm_service.rb
@@ -50,7 +50,7 @@ module Clusters
end
def helm_api
- @helm_api ||= Gitlab::Kubernetes::Helm::Api.new(kubeclient)
+ @helm_api ||= Gitlab::Kubernetes::Helm::API.new(kubeclient)
end
def install_command
diff --git a/app/views/projects/_commit_button.html.haml b/app/views/projects/_commit_button.html.haml
index b6689f4b57a..5f7ed46297b 100644
--- a/app/views/projects/_commit_button.html.haml
+++ b/app/views/projects/_commit_button.html.haml
@@ -1,5 +1,6 @@
.form-actions
- = button_tag 'Commit changes', class: 'btn commit-btn js-commit-button btn-success qa-commit-button'
+ = button_tag 'Commit changes', id: 'commit-changes', class: 'btn commit-btn js-commit-button btn-success qa-commit-button'
+
= link_to 'Cancel', cancel_path,
class: 'btn btn-cancel', data: {confirm: leave_edit_message}
diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml
index 738bca111cd..cd8407608a3 100644
--- a/app/views/projects/blob/_editor.html.haml
+++ b/app/views/projects/blob/_editor.html.haml
@@ -20,7 +20,10 @@
required: true, class: 'form-control new-file-name js-file-path-name-input', value: params[:file_name] || (should_suggest_gitlab_ci_yml? ? '.gitlab-ci.yml' : '')
= render 'template_selectors'
- if should_suggest_gitlab_ci_yml?
- = render partial: 'suggest_gitlab_ci_yml', locals: { target: '#gitlab-ci-yml-selector', dismiss_key: "suggest_gitlab_ci_yml_#{@project.id}" }
+ .js-suggest-gitlab-ci-yml{ data: { toggle: 'popover',
+ target: '#gitlab-ci-yml-selector',
+ track_label: 'suggest_gitlab_ci_yml',
+ dismiss_key: "suggest_gitlab_ci_yml_#{@project.id}" } }
.file-buttons
- if is_markdown
diff --git a/app/views/projects/blob/_suggest_gitlab_ci_yml.html.haml b/app/views/projects/blob/_suggest_gitlab_ci_yml.html.haml
deleted file mode 100644
index 6b368033c1e..00000000000
--- a/app/views/projects/blob/_suggest_gitlab_ci_yml.html.haml
+++ /dev/null
@@ -1,4 +0,0 @@
-.js-suggest-gitlab-ci-yml{ data: { toggle: 'popover',
- target: target,
- css_class: 'suggest-gitlab-ci-yml ml-4',
- dismiss_key: dismiss_key } }
diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml
index c5e3923f1e8..bf7900018c3 100644
--- a/app/views/projects/blob/new.html.haml
+++ b/app/views/projects/blob/new.html.haml
@@ -13,3 +13,8 @@
= hidden_field_tag 'content', '', id: 'file-content'
= render 'projects/commit_button', ref: @ref,
cancel_path: project_tree_path(@project, @id)
+ - if should_suggest_gitlab_ci_yml?
+ .js-suggest-gitlab-ci-yml-commit-changes{ data: { toggle: 'popover',
+ target: '#commit-changes',
+ track_label: 'suggest_commit_first_project_gitlab_ci_yml',
+ dismiss_key: "suggest_commit_first_project_gitlab_ci_yml_#{@project.id}" } }
diff --git a/changelogs/unreleased/creator-pairing-group-list-padding.yml b/changelogs/unreleased/creator-pairing-group-list-padding.yml
new file mode 100644
index 00000000000..1e1431208e4
--- /dev/null
+++ b/changelogs/unreleased/creator-pairing-group-list-padding.yml
@@ -0,0 +1,5 @@
+---
+title: Added a padding-right to items in subgroup list
+merge_request: 26791
+author:
+type: fixed
diff --git a/db/migrate/20200309105539_add_index_services_on_template.rb b/db/migrate/20200309105539_add_index_services_on_template.rb
new file mode 100644
index 00000000000..731fa04123c
--- /dev/null
+++ b/db/migrate/20200309105539_add_index_services_on_template.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddIndexServicesOnTemplate < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ # This migration is a corrective action to add the missing
+ # index_services_on_template index on staging.
+ def up
+ add_concurrent_index(:services, :template) unless index_exists?(:services, :template)
+ end
+
+ def down
+ # No reverse action as this is a corrective migration.
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index b49c68458ca..deca4b3a6d0 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2020_03_06_170531) do
+ActiveRecord::Schema.define(version: 2020_03_09_105539) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm"
diff --git a/doc/development/pipelines.md b/doc/development/pipelines.md
index ec0e5df6a92..1454f91ac20 100644
--- a/doc/development/pipelines.md
+++ b/doc/development/pipelines.md
@@ -91,7 +91,8 @@ and are as follows:
1. For `master` branch, create a pipeline (this includes on schedules, pushes, merges, etc.).
1. For tags, create a pipeline.
1. If `$GITLAB_INTERNAL` isn't set, don't create a pipeline.
- 1. For stable, auto-deploy, and security branches, create a pipeline.
+1. For stable, auto-deploy, and security branches, create a pipeline.
+1. For any other cases (e.g. when pushing a branch with no MR for it), no pipeline is created.
## `rules`, `if:` conditions and `changes:` patterns
diff --git a/doc/development/testing_guide/end_to_end/index.md b/doc/development/testing_guide/end_to_end/index.md
index 65ee2b0a759..d701af20ec3 100644
--- a/doc/development/testing_guide/end_to_end/index.md
+++ b/doc/development/testing_guide/end_to_end/index.md
@@ -183,6 +183,7 @@ Continued reading:
- [Best Practices](best_practices.md)
- [Testing with feature flags](feature_flags.md)
- [Flows](flows.md)
+- [RSpec metadata/tags](rspec_metadata_tests.md)
## Where can I ask for help?
diff --git a/doc/development/testing_guide/end_to_end/rspec_metadata_tests.md b/doc/development/testing_guide/end_to_end/rspec_metadata_tests.md
new file mode 100644
index 00000000000..abc7c88b4f2
--- /dev/null
+++ b/doc/development/testing_guide/end_to_end/rspec_metadata_tests.md
@@ -0,0 +1,13 @@
+# RSpec metadata for end-to-end tests
+
+This is a partial list of the [RSpec metadata](https://relishapp.com/rspec/rspec-core/docs/metadata/user-defined-metadata)
+(a.k.a. tags) that are used in our end-to-end tests.
+
+<!-- Please keep the tags in alphabetical order -->
+
+| Tag | Description |
+|-|-|
+| `:elasticsearch` | The test requires an Elasticsearch service. It is used by the [instance-level scenario](https://gitlab.com/gitlab-org/gitlab-qa#definitions) [`Test::Integration::Elasticsearch`](https://gitlab.com/gitlab-org/gitlab/-/blob/72b62b51bdf513e2936301cb6c7c91ec27c35b4d/qa/qa/ee/scenario/test/integration/elasticsearch.rb) to include only tests that require Elasticsearch. |
+| `:orchestrated` | The GitLab instance under test may be [configured by `gitlab-qa`](https://gitlab.com/gitlab-org/gitlab-qa/-/blob/master/docs/what_tests_can_be_run.md#orchestrated-tests) to be different to the default GitLab configuration, or `gitlab-qa` may launch additional services in separate docker containers, or both. Tests tagged with `:orchestrated` are excluded when testing environments where we can't dynamically modify GitLab's configuration (for example, Staging). |
+| `:quarantine` | The test has been [quarantined](https://about.gitlab.com/handbook/engineering/quality/guidelines/debugging-qa-test-failures/#quarantining-tests), will run in a separate job that only includes quarantined tests, and is allowed to fail. The test will be skipped in its regular job so that if it fails it will not hold up the pipeline. |
+| `:requires_admin` | The test requires an admin account. Tests with the tag are excluded when run against Canary and Production environments. |
diff --git a/doc/security/unlock_user.md b/doc/security/unlock_user.md
index 7c015525fdf..befb5d12877 100644
--- a/doc/security/unlock_user.md
+++ b/doc/security/unlock_user.md
@@ -4,7 +4,7 @@ type: howto
# How to unlock a locked user from the command line
-After six failed login attempts a user gets in a locked state.
+After ten failed login attempts a user gets in a locked state.
To unlock a locked user:
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index b078eb25b6a..77e35eee76d 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -744,9 +744,9 @@ workers:
By default, all Kubernetes pods are
[non-isolated](https://kubernetes.io/docs/concepts/services-networking/network-policies/#isolated-and-non-isolated-pods),
-and accept traffic from any source. You can use
+meaning that they will accept traffic to and from any source. You can use
[NetworkPolicy](https://kubernetes.io/docs/concepts/services-networking/network-policies/)
-to restrict connections to selected pods or namespaces.
+to restrict connections to and from selected pods, namespaces, and the Internet.
NOTE: **Note:**
You must use a Kubernetes network plugin that implements support for
@@ -767,7 +767,7 @@ networkPolicy:
The default policy deployed by the auto deploy pipeline will allow
traffic within a local namespace and from the `gitlab-managed-apps`
namespace. All other inbound connection will be blocked. Outbound
-traffic is not affected by the default policy.
+traffic (for example, to the Internet) is not affected by the default policy.
You can also provide a custom [policy specification](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.16/#networkpolicyspec-v1-networking-k8s-io)
via the `.gitlab/auto-deploy-values.yaml` file, for example:
@@ -788,6 +788,9 @@ networkPolicy:
app.gitlab.com/managed_by: gitlab
```
+For more information on how to install Network Policies, see
+[Install Cilium using GitLab CI](../../user/clusters/applications.md#install-cilium-using-gitlab-ci).
+
#### Web Application Firewall (ModSecurity) customization
> [Introduced](https://gitlab.com/gitlab-org/charts/auto-deploy-app/-/merge_requests/44) in GitLab 12.8.
diff --git a/lib/gitlab/kubernetes/helm/api.rb b/lib/gitlab/kubernetes/helm/api.rb
index 3ed07818302..3b843799d66 100644
--- a/lib/gitlab/kubernetes/helm/api.rb
+++ b/lib/gitlab/kubernetes/helm/api.rb
@@ -3,7 +3,7 @@
module Gitlab
module Kubernetes
module Helm
- class Api
+ class API
def initialize(kubeclient)
@kubeclient = kubeclient
@namespace = Gitlab::Kubernetes::Namespace.new(
diff --git a/lib/gitlab/sidekiq_logging/deduplication_logger.rb b/lib/gitlab/sidekiq_logging/deduplication_logger.rb
new file mode 100644
index 00000000000..01810e474dc
--- /dev/null
+++ b/lib/gitlab/sidekiq_logging/deduplication_logger.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module SidekiqLogging
+ class DeduplicationLogger
+ include Singleton
+ include LogsJobs
+
+ def log(job, deduplication_type)
+ payload = parse_job(job)
+ payload['job_status'] = 'deduplicated'
+ payload['message'] = "#{base_message(payload)}: deduplicated: #{deduplication_type}"
+ payload['deduplication_type'] = deduplication_type
+
+ Sidekiq.logger.info payload
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/sidekiq_logging/logs_jobs.rb b/lib/gitlab/sidekiq_logging/logs_jobs.rb
new file mode 100644
index 00000000000..55d711c54ae
--- /dev/null
+++ b/lib/gitlab/sidekiq_logging/logs_jobs.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module SidekiqLogging
+ module LogsJobs
+ def base_message(payload)
+ "#{payload['class']} JID-#{payload['jid']}"
+ end
+
+ def parse_job(job)
+ # Error information from the previous try is in the payload for
+ # displaying in the Sidekiq UI, but is very confusing in logs!
+ job = job.except('error_backtrace', 'error_class', 'error_message')
+
+ # Add process id params
+ job['pid'] = ::Process.pid
+
+ job.delete('args') unless ENV['SIDEKIQ_LOG_ARGUMENTS']
+ job['args'] = Gitlab::Utils::LogLimitedArray.log_limited_array(job['args'].map(&:to_s)) if job['args']
+
+ job
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/sidekiq_logging/structured_logger.rb b/lib/gitlab/sidekiq_logging/structured_logger.rb
index 1c36e4a4a5a..af9072ea201 100644
--- a/lib/gitlab/sidekiq_logging/structured_logger.rb
+++ b/lib/gitlab/sidekiq_logging/structured_logger.rb
@@ -6,6 +6,8 @@ require 'active_record/log_subscriber'
module Gitlab
module SidekiqLogging
class StructuredLogger
+ include LogsJobs
+
def call(job, queue)
started_time = get_time
base_payload = parse_job(job)
@@ -24,10 +26,6 @@ module Gitlab
private
- def base_message(payload)
- "#{payload['class']} JID-#{payload['jid']}"
- end
-
def add_instrumentation_keys!(job, output_payload)
output_payload.merge!(job.slice(*::Gitlab::InstrumentationHelper::KEYS))
end
@@ -76,20 +74,6 @@ module Gitlab
payload['completed_at'] = Time.now.utc.to_f
end
- def parse_job(job)
- # Error information from the previous try is in the payload for
- # displaying in the Sidekiq UI, but is very confusing in logs!
- job = job.except('error_backtrace', 'error_class', 'error_message')
-
- # Add process id params
- job['pid'] = ::Process.pid
-
- job.delete('args') unless ENV['SIDEKIQ_LOG_ARGUMENTS']
- job['args'] = Gitlab::Utils::LogLimitedArray.log_limited_array(job['args'].map(&:to_s)) if job['args']
-
- job
- end
-
def elapsed(t0)
t1 = get_time
{
diff --git a/lib/gitlab/sidekiq_middleware/duplicate_jobs.rb b/lib/gitlab/sidekiq_middleware/duplicate_jobs.rb
new file mode 100644
index 00000000000..23222430902
--- /dev/null
+++ b/lib/gitlab/sidekiq_middleware/duplicate_jobs.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'digest'
+
+module Gitlab
+ module SidekiqMiddleware
+ module DuplicateJobs
+ def self.drop_duplicates?
+ Feature.enabled?(:drop_duplicate_sidekiq_jobs)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb b/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb
index b84673c4cee..c6fb50b4610 100644
--- a/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb
+++ b/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb
@@ -66,6 +66,10 @@ module Gitlab
jid != existing_jid
end
+ def droppable?
+ idempotent? && duplicate? && DuplicateJobs.drop_duplicates?
+ end
+
private
attr_reader :queue_name, :strategy, :job
@@ -98,6 +102,14 @@ module Gitlab
def idempotency_string
"#{worker_class_name}:#{arguments.join('-')}"
end
+
+ def idempotent?
+ worker_class = worker_class_name.to_s.safe_constantize
+ return false unless worker_class
+ return false unless worker_class.respond_to?(:idempotent?)
+
+ worker_class.idempotent?
+ end
end
end
end
diff --git a/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/until_executing.rb b/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/until_executing.rb
index b8f49b67a59..674e436b714 100644
--- a/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/until_executing.rb
+++ b/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/until_executing.rb
@@ -17,6 +17,11 @@ module Gitlab
job['duplicate-of'] = duplicate_job.existing_jid
end
+ if duplicate_job.droppable?
+ Gitlab::SidekiqLogging::DeduplicationLogger.instance.log(job, "dropped until executing")
+ return false
+ end
+
yield
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 7a1a8eb2656..5892d3b65bc 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -24211,6 +24211,12 @@ msgstr ""
msgid "suggestPipeline|1/2: Choose a template"
msgstr ""
+msgid "suggestPipeline|2/2: Commit your changes"
+msgstr ""
+
+msgid "suggestPipeline|Commit the changes and your pipeline will automatically run for the first time."
+msgstr ""
+
msgid "suggestPipeline|We recommend the %{boldStart}Code Quality%{boldEnd} template, which will add a report widget to your Merge Requests. This way you’ll learn about code quality degradations much sooner. %{footerStart} Goodbye technical debt! %{footerEnd}"
msgstr ""
diff --git a/spec/frontend/blob/suggest_gitlab_ci_yml/components/popover_spec.js b/spec/frontend/blob/suggest_gitlab_ci_yml/components/popover_spec.js
index 0170ef927cf..2c7891e4b1a 100644
--- a/spec/frontend/blob/suggest_gitlab_ci_yml/components/popover_spec.js
+++ b/spec/frontend/blob/suggest_gitlab_ci_yml/components/popover_spec.js
@@ -1,18 +1,25 @@
import { shallowMount } from '@vue/test-utils';
import Popover from '~/blob/suggest_gitlab_ci_yml/components/popover.vue';
import Cookies from 'js-cookie';
+import * as utils from '~/lib/utils/common_utils';
-const popoverTarget = 'gitlab-ci-yml-selector';
+jest.mock('~/lib/utils/common_utils', () => ({
+ ...jest.requireActual('~/lib/utils/common_utils'),
+ scrollToElement: jest.fn(),
+}));
+
+const target = 'gitlab-ci-yml-selector';
const dismissKey = 'suggest_gitlab_ci_yml_99';
+const defaultTrackLabel = 'suggest_gitlab_ci_yml';
describe('Suggest gitlab-ci.yml Popover', () => {
let wrapper;
- function createWrapper() {
+ function createWrapper(trackLabel) {
wrapper = shallowMount(Popover, {
propsData: {
- target: popoverTarget,
- cssClass: 'js-class',
+ target,
+ trackLabel,
dismissKey,
},
});
@@ -25,7 +32,7 @@ describe('Suggest gitlab-ci.yml Popover', () => {
describe('when no dismiss cookie is set', () => {
beforeEach(() => {
- createWrapper();
+ createWrapper(defaultTrackLabel);
});
it('sets popoverDismissed to false', () => {
@@ -36,11 +43,26 @@ describe('Suggest gitlab-ci.yml Popover', () => {
describe('when the dismiss cookie is set', () => {
beforeEach(() => {
Cookies.set(dismissKey, true);
- createWrapper();
+ createWrapper(defaultTrackLabel);
});
it('sets popoverDismissed to true', () => {
expect(wrapper.vm.popoverDismissed).toEqual(true);
});
+
+ beforeEach(() => {
+ Cookies.remove(dismissKey);
+ });
+ });
+
+ describe('when the popover is mounted with the trackLabel of the Confirm button popover at the bottom of the page', () => {
+ it('calls scrollToElement so that the Confirm button and popover will be in sight', () => {
+ const scrollToElementSpy = jest.spyOn(utils, 'scrollToElement');
+ const commitTrackLabel = 'suggest_commit_first_project_gitlab_ci_yml';
+
+ createWrapper(commitTrackLabel);
+
+ expect(scrollToElementSpy).toHaveBeenCalled();
+ });
});
});
diff --git a/spec/frontend/diffs/components/tree_list_spec.js b/spec/frontend/diffs/components/tree_list_spec.js
new file mode 100644
index 00000000000..f78c5f25ee7
--- /dev/null
+++ b/spec/frontend/diffs/components/tree_list_spec.js
@@ -0,0 +1,138 @@
+import Vuex from 'vuex';
+import { mount, createLocalVue } from '@vue/test-utils';
+import TreeList from '~/diffs/components/tree_list.vue';
+import createStore from '~/diffs/store/modules';
+
+describe('Diffs tree list component', () => {
+ let wrapper;
+ const getFileRows = () => wrapper.findAll('.file-row');
+ const localVue = createLocalVue();
+ localVue.use(Vuex);
+
+ const createComponent = state => {
+ const store = new Vuex.Store({
+ modules: {
+ diffs: createStore(),
+ },
+ });
+
+ // Setup initial state
+ store.state.diffs.diffFiles.push('test');
+ store.state.diffs = {
+ addedLines: 10,
+ removedLines: 20,
+ ...store.state.diffs,
+ ...state,
+ };
+
+ wrapper = mount(TreeList, {
+ store,
+ localVue,
+ propsData: { hideFileStats: false },
+ });
+ };
+
+ beforeEach(() => {
+ localStorage.removeItem('mr_diff_tree_list');
+
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders empty text', () => {
+ expect(wrapper.text()).toContain('No files found');
+ });
+
+ describe('with files', () => {
+ beforeEach(() => {
+ const treeEntries = {
+ 'index.js': {
+ addedLines: 0,
+ changed: true,
+ deleted: false,
+ fileHash: 'test',
+ key: 'index.js',
+ name: 'index.js',
+ path: 'app/index.js',
+ removedLines: 0,
+ tempFile: true,
+ type: 'blob',
+ parentPath: 'app',
+ },
+ app: {
+ key: 'app',
+ path: 'app',
+ name: 'app',
+ type: 'tree',
+ tree: [],
+ },
+ };
+
+ createComponent({
+ treeEntries,
+ tree: [treeEntries['index.js'], treeEntries.app],
+ });
+
+ return wrapper.vm.$nextTick();
+ });
+
+ it('renders tree', () => {
+ expect(getFileRows()).toHaveLength(2);
+ expect(
+ getFileRows()
+ .at(0)
+ .text(),
+ ).toContain('index.js');
+ expect(
+ getFileRows()
+ .at(1)
+ .text(),
+ ).toContain('app');
+ });
+
+ it('hides file stats', () => {
+ wrapper.setProps({ hideFileStats: true });
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.find('.file-row-stats').exists()).toBe(false);
+ });
+ });
+
+ it('calls toggleTreeOpen when clicking folder', () => {
+ jest.spyOn(wrapper.vm.$store, 'dispatch').mockReturnValue(undefined);
+
+ getFileRows()
+ .at(1)
+ .trigger('click');
+
+ expect(wrapper.vm.$store.dispatch).toHaveBeenCalledWith('diffs/toggleTreeOpen', 'app');
+ });
+
+ it('calls scrollToFile when clicking blob', () => {
+ jest.spyOn(wrapper.vm.$store, 'dispatch').mockReturnValue(undefined);
+
+ wrapper.find('.file-row').trigger('click');
+
+ expect(wrapper.vm.$store.dispatch).toHaveBeenCalledWith('diffs/scrollToFile', 'app/index.js');
+ });
+
+ it('renders as file list when renderTreeList is false', () => {
+ wrapper.vm.$store.state.diffs.renderTreeList = false;
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(getFileRows()).toHaveLength(1);
+ });
+ });
+
+ it('renders file paths when renderTreeList is false', () => {
+ wrapper.vm.$store.state.diffs.renderTreeList = false;
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.find('.file-row').text()).toContain('index.js');
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/diffs/components/tree_list_spec.js b/spec/javascripts/diffs/components/tree_list_spec.js
deleted file mode 100644
index 0a6e433551c..00000000000
--- a/spec/javascripts/diffs/components/tree_list_spec.js
+++ /dev/null
@@ -1,126 +0,0 @@
-import Vue from 'vue';
-import Vuex from 'vuex';
-import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
-import TreeList from '~/diffs/components/tree_list.vue';
-import createStore from '~/diffs/store/modules';
-
-describe('Diffs tree list component', () => {
- let Component;
- let vm;
-
- beforeAll(() => {
- Component = Vue.extend(TreeList);
- });
-
- beforeEach(() => {
- Vue.use(Vuex);
-
- const store = new Vuex.Store({
- modules: {
- diffs: createStore(),
- },
- });
-
- // Setup initial state
- store.state.diffs.addedLines = 10;
- store.state.diffs.removedLines = 20;
- store.state.diffs.diffFiles.push('test');
-
- localStorage.removeItem('mr_diff_tree_list');
-
- vm = mountComponentWithStore(Component, { store, props: { hideFileStats: false } });
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('renders empty text', () => {
- expect(vm.$el.textContent).toContain('No files found');
- });
-
- describe('with files', () => {
- beforeEach(done => {
- Object.assign(vm.$store.state.diffs.treeEntries, {
- 'index.js': {
- addedLines: 0,
- changed: true,
- deleted: false,
- fileHash: 'test',
- key: 'index.js',
- name: 'index.js',
- path: 'app/index.js',
- removedLines: 0,
- tempFile: true,
- type: 'blob',
- parentPath: 'app',
- },
- app: {
- key: 'app',
- path: 'app',
- name: 'app',
- type: 'tree',
- tree: [],
- },
- });
- vm.$store.state.diffs.tree = [
- vm.$store.state.diffs.treeEntries['index.js'],
- vm.$store.state.diffs.treeEntries.app,
- ];
-
- vm.$nextTick(done);
- });
-
- it('renders tree', () => {
- expect(vm.$el.querySelectorAll('.file-row').length).toBe(2);
- expect(vm.$el.querySelectorAll('.file-row')[0].textContent).toContain('index.js');
- expect(vm.$el.querySelectorAll('.file-row')[1].textContent).toContain('app');
- });
-
- it('hides file stats', done => {
- vm.hideFileStats = true;
-
- vm.$nextTick(() => {
- expect(vm.$el.querySelector('.file-row-stats')).toBe(null);
-
- done();
- });
- });
-
- it('calls toggleTreeOpen when clicking folder', () => {
- spyOn(vm.$store, 'dispatch').and.stub();
-
- vm.$el.querySelectorAll('.file-row')[1].click();
-
- expect(vm.$store.dispatch).toHaveBeenCalledWith('diffs/toggleTreeOpen', 'app');
- });
-
- it('calls scrollToFile when clicking blob', () => {
- spyOn(vm.$store, 'dispatch').and.stub();
-
- vm.$el.querySelector('.file-row').click();
-
- expect(vm.$store.dispatch).toHaveBeenCalledWith('diffs/scrollToFile', 'app/index.js');
- });
-
- it('renders as file list when renderTreeList is false', done => {
- vm.$store.state.diffs.renderTreeList = false;
-
- vm.$nextTick(() => {
- expect(vm.$el.querySelectorAll('.file-row').length).toBe(1);
-
- done();
- });
- });
-
- it('renders file paths when renderTreeList is false', done => {
- vm.$store.state.diffs.renderTreeList = false;
-
- vm.$nextTick(() => {
- expect(vm.$el.querySelector('.file-row').textContent).toContain('index.js');
-
- done();
- });
- });
- });
-});
diff --git a/spec/lib/gitlab/kubernetes/helm/api_spec.rb b/spec/lib/gitlab/kubernetes/helm/api_spec.rb
index e493acd7bad..8147990ecc3 100644
--- a/spec/lib/gitlab/kubernetes/helm/api_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/api_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Gitlab::Kubernetes::Helm::Api do
+describe Gitlab::Kubernetes::Helm::API do
let(:client) { double('kubernetes client') }
let(:helm) { described_class.new(client) }
let(:gitlab_namespace) { Gitlab::Kubernetes::Helm::NAMESPACE }
diff --git a/spec/lib/gitlab/sidekiq_logging/deduplication_logger_spec.rb b/spec/lib/gitlab/sidekiq_logging/deduplication_logger_spec.rb
new file mode 100644
index 00000000000..3cc5c0bed1b
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_logging/deduplication_logger_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::SidekiqLogging::DeduplicationLogger do
+ describe '#log_deduplication' do
+ let(:job) do
+ {
+ 'class' => 'TestWorker',
+ 'args' => [1234, 'hello', { 'key' => 'value' }],
+ 'jid' => 'da883554ee4fe414012f5f42',
+ 'correlation_id' => 'cid',
+ 'duplicate-of' => 'other_jid'
+ }
+ end
+
+ it 'logs a deduplication message to the sidekiq logger' do
+ expected_payload = {
+ 'job_status' => 'deduplicated',
+ 'message' => "#{job['class']} JID-#{job['jid']}: deduplicated: a fancy strategy",
+ 'deduplication_type' => 'a fancy strategy'
+ }
+ expect(Sidekiq.logger).to receive(:info).with(a_hash_including(expected_payload)).and_call_original
+
+ described_class.instance.log(job, "a fancy strategy")
+ end
+
+ it "does not modify the job" do
+ expect { described_class.instance.log(job, "a fancy strategy") }
+ .not_to change { job }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job_spec.rb b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job_spec.rb
index 2334439461e..058e0737a25 100644
--- a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
describe Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob, :clean_gitlab_redis_queues do
+ using RSpec::Parameterized::TableSyntax
+
subject(:duplicate_job) do
described_class.new(job, queue)
end
@@ -110,6 +112,36 @@ describe Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob, :clean_gitlab_r
end
end
+ describe 'droppable?' do
+ where(:idempotent, :duplicate, :feature_enabled) do
+ # [true, false].repeated_permutation(3)
+ [[true, true, true],
+ [true, true, false],
+ [true, false, true],
+ [true, false, false],
+ [false, true, true],
+ [false, true, false],
+ [false, false, true],
+ [false, false, false]]
+ end
+
+ with_them do
+ before do
+ allow(AuthorizedProjectsWorker).to receive(:idempotent?).and_return(idempotent)
+ allow(duplicate_job).to receive(:duplicate?).and_return(duplicate)
+ stub_feature_flags(drop_duplicate_sidekiq_jobs: feature_enabled)
+ end
+
+ it 'is droppable when all conditions are met' do
+ if idempotent && duplicate && feature_enabled
+ expect(duplicate_job).to be_droppable
+ else
+ expect(duplicate_job).not_to be_droppable
+ end
+ end
+ end
+ end
+
def set_idempotency_key(key, value = '1')
Sidekiq.redis { |r| r.set(key, value) }
end
diff --git a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/until_executing_spec.rb b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/until_executing_spec.rb
index f40e829f9a5..31b51260ebd 100644
--- a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/until_executing_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/until_executing_spec.rb
@@ -10,14 +10,21 @@ describe Gitlab::SidekiqMiddleware::DuplicateJobs::Strategies::UntilExecuting do
subject(:strategy) { described_class.new(fake_duplicate_job) }
describe '#schedule' do
+ before do
+ allow(Gitlab::SidekiqLogging::DeduplicationLogger.instance).to receive(:log)
+ end
+
it 'checks for duplicates before yielding' do
expect(fake_duplicate_job).to receive(:check!).ordered.and_return('a jid')
expect(fake_duplicate_job).to receive(:duplicate?).ordered.and_return(false)
+ expect(fake_duplicate_job).to receive(:droppable?).ordered.and_return(false)
+
expect { |b| strategy.schedule({}, &b) }.to yield_control
end
it 'adds the jid of the existing job to the job hash' do
allow(fake_duplicate_job).to receive(:check!).and_return('the jid')
+ allow(fake_duplicate_job).to receive(:droppable?).and_return(true)
job_hash = {}
expect(fake_duplicate_job).to receive(:duplicate?).and_return(true)
@@ -27,6 +34,33 @@ describe Gitlab::SidekiqMiddleware::DuplicateJobs::Strategies::UntilExecuting do
expect(job_hash).to include('duplicate-of' => 'the jid')
end
+
+ context "when the job is droppable" do
+ before do
+ allow(fake_duplicate_job).to receive(:check!).and_return('the jid')
+ allow(fake_duplicate_job).to receive(:duplicate?).and_return(true)
+ allow(fake_duplicate_job).to receive(:existing_jid).and_return('the jid')
+ allow(fake_duplicate_job).to receive(:droppable?).and_return(true)
+ end
+
+ it 'drops the job' do
+ schedule_result = nil
+
+ expect(fake_duplicate_job).to receive(:droppable?).and_return(true)
+
+ expect { |b| schedule_result = strategy.schedule({}, &b) }.not_to yield_control
+ expect(schedule_result).to be(false)
+ end
+
+ it 'logs that the job wass dropped' do
+ fake_logger = instance_double(Gitlab::SidekiqLogging::DeduplicationLogger)
+
+ expect(Gitlab::SidekiqLogging::DeduplicationLogger).to receive(:instance).and_return(fake_logger)
+ expect(fake_logger).to receive(:log).with(a_hash_including({ 'jid' => 'new jid' }), 'dropped until executing')
+
+ strategy.schedule({ 'jid' => 'new jid' }) {}
+ end
+ end
end
describe '#perform' do
diff --git a/spec/lib/gitlab/url_blocker_spec.rb b/spec/lib/gitlab/url_blocker_spec.rb
index 97859c82e9e..f8bfcc6c99a 100644
--- a/spec/lib/gitlab/url_blocker_spec.rb
+++ b/spec/lib/gitlab/url_blocker_spec.rb
@@ -501,64 +501,20 @@ describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
it_behaves_like 'dns rebinding checks'
end
end
-
- context 'with ip ranges in whitelist' do
- let(:ipv4_range) { '127.0.0.0/28' }
- let(:ipv6_range) { 'fd84:6d02:f6d8:c89e::/124' }
-
- let(:whitelist) do
- [
- ipv4_range,
- ipv6_range
- ]
- end
-
- it 'blocks ipv4 range when not in whitelist' do
- stub_application_setting(outbound_local_requests_whitelist: [])
-
- IPAddr.new(ipv4_range).to_range.to_a.each do |ip|
- expect(described_class).to be_blocked_url("http://#{ip}",
- url_blocker_attributes)
- end
- end
-
- it 'allows all ipv4s in the range when in whitelist' do
- IPAddr.new(ipv4_range).to_range.to_a.each do |ip|
- expect(described_class).not_to be_blocked_url("http://#{ip}",
- url_blocker_attributes)
- end
- end
-
- it 'blocks ipv6 range when not in whitelist' do
- stub_application_setting(outbound_local_requests_whitelist: [])
-
- IPAddr.new(ipv6_range).to_range.to_a.each do |ip|
- expect(described_class).to be_blocked_url("http://[#{ip}]",
- url_blocker_attributes)
- end
- end
-
- it 'allows all ipv6s in the range when in whitelist' do
- IPAddr.new(ipv6_range).to_range.to_a.each do |ip|
- expect(described_class).not_to be_blocked_url("http://[#{ip}]",
- url_blocker_attributes)
- end
- end
-
- it 'blocks IPs outside the range' do
- expect(described_class).to be_blocked_url("http://[fd84:6d02:f6d8:c89e:0:0:1:f]",
- url_blocker_attributes)
-
- expect(described_class).to be_blocked_url("http://127.0.1.15",
- url_blocker_attributes)
- end
- end
end
end
- def stub_domain_resolv(domain, ip, &block)
- address = double(ip_address: ip, ipv4_private?: true, ipv6_link_local?: false, ipv4_loopback?: false, ipv6_loopback?: false, ipv4?: false)
- allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([address])
+ def stub_domain_resolv(domain, ip, port = 80, &block)
+ address = instance_double(Addrinfo,
+ ip_address: ip,
+ ipv4_private?: true,
+ ipv6_linklocal?: false,
+ ipv4_loopback?: false,
+ ipv6_loopback?: false,
+ ipv4?: false,
+ ip_port: port
+ )
+ allow(Addrinfo).to receive(:getaddrinfo).with(domain, port, any_args).and_return([address])
allow(address).to receive(:ipv6_v4mapped?).and_return(false)
yield
diff --git a/spec/lib/gitlab/url_blockers/url_whitelist_spec.rb b/spec/lib/gitlab/url_blockers/url_whitelist_spec.rb
index 906e0f0ba3d..64d804e8541 100644
--- a/spec/lib/gitlab/url_blockers/url_whitelist_spec.rb
+++ b/spec/lib/gitlab/url_blockers/url_whitelist_spec.rb
@@ -68,5 +68,51 @@ describe Gitlab::UrlBlockers::UrlWhitelist do
it 'returns false when ip is blank' do
expect(described_class).not_to be_ip_whitelisted(nil)
end
+
+ context 'with ip ranges in whitelist' do
+ let(:ipv4_range) { '127.0.0.0/28' }
+ let(:ipv6_range) { 'fd84:6d02:f6d8:c89e::/124' }
+
+ let(:whitelist) do
+ [
+ ipv4_range,
+ ipv6_range
+ ]
+ end
+
+ it 'does not whitelist ipv4 range when not in whitelist' do
+ stub_application_setting(outbound_local_requests_whitelist: [])
+
+ IPAddr.new(ipv4_range).to_range.to_a.each do |ip|
+ expect(described_class).not_to be_ip_whitelisted(ip.to_s)
+ end
+ end
+
+ it 'whitelists all ipv4s in the range when in whitelist' do
+ IPAddr.new(ipv4_range).to_range.to_a.each do |ip|
+ expect(described_class).to be_ip_whitelisted(ip.to_s)
+ end
+ end
+
+ it 'does not whitelist ipv6 range when not in whitelist' do
+ stub_application_setting(outbound_local_requests_whitelist: [])
+
+ IPAddr.new(ipv6_range).to_range.to_a.each do |ip|
+ expect(described_class).not_to be_ip_whitelisted(ip.to_s)
+ end
+ end
+
+ it 'whitelists all ipv6s in the range when in whitelist' do
+ IPAddr.new(ipv6_range).to_range.to_a.each do |ip|
+ expect(described_class).to be_ip_whitelisted(ip.to_s)
+ end
+ end
+
+ it 'does not whitelist IPs outside the range' do
+ expect(described_class).not_to be_ip_whitelisted("fd84:6d02:f6d8:c89e:0:0:1:f")
+
+ expect(described_class).not_to be_ip_whitelisted("127.0.1.15")
+ end
+ end
end
end
diff --git a/spec/services/clusters/applications/check_installation_progress_service_spec.rb b/spec/services/clusters/applications/check_installation_progress_service_spec.rb
index 2f224d40920..4b8db405101 100644
--- a/spec/services/clusters/applications/check_installation_progress_service_spec.rb
+++ b/spec/services/clusters/applications/check_installation_progress_service_spec.rb
@@ -144,7 +144,7 @@ describe Clusters::Applications::CheckInstallationProgressService, '#execute' do
end
it 'removes the installation POD' do
- expect_next_instance_of(Gitlab::Kubernetes::Helm::Api) do |instance|
+ expect_next_instance_of(Gitlab::Kubernetes::Helm::API) do |instance|
expect(instance).to receive(:delete_pod!).with(kind_of(String)).once
end
expect(service).to receive(:remove_installation_pod).and_call_original
diff --git a/spec/services/clusters/applications/check_uninstall_progress_service_spec.rb b/spec/services/clusters/applications/check_uninstall_progress_service_spec.rb
index 93557c6b229..ffb658330d3 100644
--- a/spec/services/clusters/applications/check_uninstall_progress_service_spec.rb
+++ b/spec/services/clusters/applications/check_uninstall_progress_service_spec.rb
@@ -48,7 +48,7 @@ describe Clusters::Applications::CheckUninstallProgressService do
let(:phase) { Gitlab::Kubernetes::Pod::SUCCEEDED }
before do
- expect_next_instance_of(Gitlab::Kubernetes::Helm::Api) do |instance|
+ expect_next_instance_of(Gitlab::Kubernetes::Helm::API) do |instance|
expect(instance).to receive(:delete_pod!).with(kind_of(String)).once
end
expect(service).to receive(:pod_phase).once.and_return(phase)
diff --git a/spec/services/clusters/applications/install_service_spec.rb b/spec/services/clusters/applications/install_service_spec.rb
index 9e1ae5e8742..2441cc595a3 100644
--- a/spec/services/clusters/applications/install_service_spec.rb
+++ b/spec/services/clusters/applications/install_service_spec.rb
@@ -7,7 +7,7 @@ describe Clusters::Applications::InstallService do
let(:application) { create(:clusters_applications_helm, :scheduled) }
let!(:install_command) { application.install_command }
let(:service) { described_class.new(application) }
- let(:helm_client) { instance_double(Gitlab::Kubernetes::Helm::Api) }
+ let(:helm_client) { instance_double(Gitlab::Kubernetes::Helm::API) }
before do
allow(service).to receive(:install_command).and_return(install_command)
diff --git a/spec/services/clusters/applications/patch_service_spec.rb b/spec/services/clusters/applications/patch_service_spec.rb
index c6d0fee43c4..dc9843a5116 100644
--- a/spec/services/clusters/applications/patch_service_spec.rb
+++ b/spec/services/clusters/applications/patch_service_spec.rb
@@ -7,7 +7,7 @@ describe Clusters::Applications::PatchService do
let(:application) { create(:clusters_applications_knative, :scheduled) }
let!(:update_command) { application.update_command }
let(:service) { described_class.new(application) }
- let(:helm_client) { instance_double(Gitlab::Kubernetes::Helm::Api) }
+ let(:helm_client) { instance_double(Gitlab::Kubernetes::Helm::API) }
before do
allow(service).to receive(:update_command).and_return(update_command)
diff --git a/spec/services/clusters/applications/uninstall_service_spec.rb b/spec/services/clusters/applications/uninstall_service_spec.rb
index 16497d752b2..6d7f0478b20 100644
--- a/spec/services/clusters/applications/uninstall_service_spec.rb
+++ b/spec/services/clusters/applications/uninstall_service_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
describe Clusters::Applications::UninstallService, '#execute' do
let(:application) { create(:clusters_applications_prometheus, :scheduled) }
let(:service) { described_class.new(application) }
- let(:helm_client) { instance_double(Gitlab::Kubernetes::Helm::Api) }
+ let(:helm_client) { instance_double(Gitlab::Kubernetes::Helm::API) }
let(:worker_class) { Clusters::Applications::WaitForUninstallAppWorker }
before do
diff --git a/spec/services/clusters/applications/upgrade_service_spec.rb b/spec/services/clusters/applications/upgrade_service_spec.rb
index 48fa79eeb97..86fb06375f1 100644
--- a/spec/services/clusters/applications/upgrade_service_spec.rb
+++ b/spec/services/clusters/applications/upgrade_service_spec.rb
@@ -7,7 +7,7 @@ describe Clusters::Applications::UpgradeService do
let(:application) { create(:clusters_applications_helm, :scheduled) }
let!(:install_command) { application.install_command }
let(:service) { described_class.new(application) }
- let(:helm_client) { instance_double(Gitlab::Kubernetes::Helm::Api) }
+ let(:helm_client) { instance_double(Gitlab::Kubernetes::Helm::API) }
before do
allow(service).to receive(:install_command).and_return(install_command)