Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/ide/components/jobs/detail.vue4
-rw-r--r--app/assets/javascripts/registry/explorer/index.js2
-rw-r--r--app/assets/javascripts/registry/explorer/pages/details.vue3
-rw-r--r--app/assets/javascripts/registry/explorer/pages/list.vue8
-rw-r--r--app/assets/javascripts/registry/explorer/router.js12
-rw-r--r--app/assets/javascripts/registry/explorer/stores/index.js1
-rw-r--r--app/assets/javascripts/snippets/components/snippet_header.vue34
-rw-r--r--app/assets/stylesheets/page_bundles/_ide_mixins.scss1
-rw-r--r--app/assets/stylesheets/page_bundles/ide.scss12
-rw-r--r--app/assets/stylesheets/page_bundles/themes/_dark.scss75
-rw-r--r--app/graphql/mutations/alert_management/base.rb35
-rw-r--r--app/graphql/mutations/alert_management/update_alert_status.rb34
-rw-r--r--app/graphql/types/alert_management/alert_type.rb25
-rw-r--r--app/graphql/types/mutation_type.rb1
-rw-r--r--app/models/alert_management/alert.rb6
-rw-r--r--app/models/ci/persistent_ref.rb12
-rw-r--r--app/policies/project_policy.rb1
-rw-r--r--app/presenters/ci/build_runner_presenter.rb18
-rw-r--r--app/services/alert_management/update_alert_status_service.rb34
-rw-r--r--app/services/spam/spam_action_service.rb5
-rw-r--r--app/views/projects/alert_management/details.html.haml3
-rw-r--r--changelogs/unreleased/212213-cablett-allowlist-gitlab-team-member.yml5
-rw-r--r--changelogs/unreleased/212592-disable-binary-edit.yml5
-rw-r--r--changelogs/unreleased/214542-graphql-status-mutation.yml5
-rw-r--r--changelogs/unreleased/expose-more-fields-in-alert-management-alert-graphql.yml5
-rw-r--r--changelogs/unreleased/kubeclient-create-or-update.yml5
-rw-r--r--changelogs/unreleased/remove-sidekiq-rake-tasks.yml5
-rw-r--r--doc/administration/reference_architectures/index.md27
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql71
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json235
-rw-r--r--doc/api/graphql/reference/index.md15
-rw-r--r--doc/ci/pipelines/index.md12
-rw-r--r--doc/install/aws/index.md54
-rw-r--r--lib/gitlab/graphql/authorize/authorize_field_service.rb8
-rw-r--r--lib/gitlab/kubernetes/helm/api.rb6
-rw-r--r--lib/gitlab/kubernetes/kube_client.rb32
-rw-r--r--lib/gitlab/sidekiq_config/cli_methods.rb3
-rw-r--r--lib/tasks/sidekiq.rake38
-rw-r--r--locale/gitlab.pot12
-rw-r--r--package.json2
-rw-r--r--spec/frontend/registry/explorer/pages/details_spec.js11
-rw-r--r--spec/frontend/registry/explorer/pages/list_spec.js89
-rw-r--r--spec/frontend/registry/explorer/stubs.js5
-rw-r--r--spec/frontend/snippets/components/snippet_header_spec.js52
-rw-r--r--spec/graphql/mutations/alert_management/update_alert_status_spec.rb73
-rw-r--r--spec/graphql/types/alert_management/alert_type_spec.rb5
-rw-r--r--spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb10
-rw-r--r--spec/lib/gitlab/kubernetes/helm/api_spec.rb25
-rw-r--r--spec/lib/gitlab/kubernetes/kube_client_spec.rb16
-rw-r--r--spec/lib/gitlab/sidekiq_config/cli_methods_spec.rb8
-rw-r--r--spec/models/alert_management/alert_spec.rb26
-rw-r--r--spec/models/ci/persistent_ref_spec.rb12
-rw-r--r--spec/policies/alert_management/alert_policy_spec.rb9
-rw-r--r--spec/presenters/ci/build_runner_presenter_spec.rb81
-rw-r--r--spec/requests/api/graphql/project/alert_management/alerts_spec.rb10
-rw-r--r--spec/services/alert_management/update_alert_status_service_spec.rb29
-rw-r--r--spec/services/clusters/gcp/finalize_creation_service_spec.rb3
-rw-r--r--spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb1
-rw-r--r--spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb12
-rw-r--r--spec/services/spam/spam_action_service_spec.rb12
-rw-r--r--spec/support/helpers/kubernetes_helpers.rb24
-rw-r--r--yarn.lock8
64 files changed, 1021 insertions, 377 deletions
diff --git a/Gemfile b/Gemfile
index f37e85c3007..cb541c868fe 100644
--- a/Gemfile
+++ b/Gemfile
@@ -419,7 +419,7 @@ end
gem 'octokit', '~> 4.15'
# https://gitlab.com/gitlab-org/gitlab/issues/207207
-gem 'gitlab-mail_room', '~> 0.0.3', require: 'mail_room'
+gem 'gitlab-mail_room', '~> 0.0.4', require: 'mail_room'
gem 'email_reply_trimmer', '~> 0.1'
gem 'html2text'
diff --git a/Gemfile.lock b/Gemfile.lock
index e68b0d9e819..8a65cdc8cc6 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -391,7 +391,7 @@ GEM
opentracing (~> 0.4)
redis (> 3.0.0, < 5.0.0)
gitlab-license (1.0.0)
- gitlab-mail_room (0.0.3)
+ gitlab-mail_room (0.0.4)
gitlab-markup (1.7.0)
gitlab-net-dns (0.9.1)
gitlab-puma (4.3.3.gitlab.2)
@@ -1244,7 +1244,7 @@ DEPENDENCIES
gitlab-chronic (~> 0.10.5)
gitlab-labkit (= 0.12.0)
gitlab-license (~> 1.0)
- gitlab-mail_room (~> 0.0.3)
+ gitlab-mail_room (~> 0.0.4)
gitlab-markup (~> 1.7.0)
gitlab-net-dns (~> 0.9.1)
gitlab-puma (~> 4.3.3.gitlab.2)
diff --git a/app/assets/javascripts/ide/components/jobs/detail.vue b/app/assets/javascripts/ide/components/jobs/detail.vue
index 504391ffdc7..975d54c7a4e 100644
--- a/app/assets/javascripts/ide/components/jobs/detail.vue
+++ b/app/assets/javascripts/ide/components/jobs/detail.vue
@@ -79,7 +79,7 @@ export default {
<icon name="chevron-left" /> {{ __('View jobs') }}
</button>
</header>
- <div class="top-bar d-flex border-left-0">
+ <div class="top-bar d-flex border-left-0 mr-3">
<job-description :job="detailJob" />
<div class="controllers ml-auto">
<a
@@ -97,7 +97,7 @@ export default {
<scroll-button :disabled="isScrolledToBottom" direction="down" @click="scrollDown" />
</div>
</div>
- <pre ref="buildTrace" class="build-trace mb-0 h-100" @scroll="scrollBuildLog">
+ <pre ref="buildTrace" class="build-trace mb-0 h-100 mr-3" @scroll="scrollBuildLog">
<code
v-show="!detailJob.isLoading"
class="bash"
diff --git a/app/assets/javascripts/registry/explorer/index.js b/app/assets/javascripts/registry/explorer/index.js
index 9269aa074f8..2bba3ee4ff9 100644
--- a/app/assets/javascripts/registry/explorer/index.js
+++ b/app/assets/javascripts/registry/explorer/index.js
@@ -19,7 +19,7 @@ export default () => {
const { endpoint } = el.dataset;
const store = createStore();
- const router = createRouter(endpoint, store);
+ const router = createRouter(endpoint);
store.dispatch('setInitialState', el.dataset);
const attachMainComponent = () =>
diff --git a/app/assets/javascripts/registry/explorer/pages/details.vue b/app/assets/javascripts/registry/explorer/pages/details.vue
index 1d4dcd9a7a2..df29ee44419 100644
--- a/app/assets/javascripts/registry/explorer/pages/details.vue
+++ b/app/assets/javascripts/registry/explorer/pages/details.vue
@@ -157,6 +157,9 @@ export default {
return config;
},
},
+ mounted() {
+ this.requestTagsList({ params: this.$route.params.id });
+ },
methods: {
...mapActions(['requestTagsList', 'requestDeleteTag', 'requestDeleteTags']),
setModalDescription(itemIndex = -1) {
diff --git a/app/assets/javascripts/registry/explorer/pages/list.vue b/app/assets/javascripts/registry/explorer/pages/list.vue
index 8923c305b2d..e932544feb8 100644
--- a/app/assets/javascripts/registry/explorer/pages/list.vue
+++ b/app/assets/javascripts/registry/explorer/pages/list.vue
@@ -103,8 +103,16 @@ export default {
: DELETE_IMAGE_ERROR_MESSAGE;
},
},
+ mounted() {
+ this.loadImageList(this.$route.name);
+ },
methods: {
...mapActions(['requestImagesList', 'requestDeleteImage']),
+ loadImageList(fromName) {
+ if (!fromName || !this.images?.length) {
+ this.requestImagesList();
+ }
+ },
deleteImage(item) {
this.track('click_button');
this.itemToDelete = item;
diff --git a/app/assets/javascripts/registry/explorer/router.js b/app/assets/javascripts/registry/explorer/router.js
index 28df3177df4..478eaca1a68 100644
--- a/app/assets/javascripts/registry/explorer/router.js
+++ b/app/assets/javascripts/registry/explorer/router.js
@@ -7,7 +7,7 @@ import { decodeAndParse } from './utils';
Vue.use(VueRouter);
-export default function createRouter(base, store) {
+export default function createRouter(base) {
const router = new VueRouter({
base,
mode: 'history',
@@ -20,12 +20,6 @@ export default function createRouter(base, store) {
nameGenerator: () => s__('ContainerRegistry|Container Registry'),
root: true,
},
- beforeEnter: (to, from, next) => {
- if (!from.name || !store.state.images?.length) {
- store.dispatch('requestImagesList');
- }
- next();
- },
},
{
name: 'details',
@@ -34,10 +28,6 @@ export default function createRouter(base, store) {
meta: {
nameGenerator: route => decodeAndParse(route.params.id).name,
},
- beforeEnter: (to, from, next) => {
- store.dispatch('requestTagsList', { params: to.params.id });
- next();
- },
},
],
});
diff --git a/app/assets/javascripts/registry/explorer/stores/index.js b/app/assets/javascripts/registry/explorer/stores/index.js
index b3ff2e6e002..153032e37d3 100644
--- a/app/assets/javascripts/registry/explorer/stores/index.js
+++ b/app/assets/javascripts/registry/explorer/stores/index.js
@@ -15,4 +15,5 @@ export const createStore = () =>
mutations,
});
+// Deprecated and to be removed
export default createStore();
diff --git a/app/assets/javascripts/snippets/components/snippet_header.vue b/app/assets/javascripts/snippets/components/snippet_header.vue
index 615983ed3cf..46ab04c9b19 100644
--- a/app/assets/javascripts/snippets/components/snippet_header.vue
+++ b/app/assets/javascripts/snippets/components/snippet_header.vue
@@ -10,6 +10,7 @@ import {
GlDropdown,
GlDropdownItem,
GlButton,
+ GlTooltipDirective,
} from '@gitlab/ui';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
@@ -30,6 +31,9 @@ export default {
TimeAgoTooltip,
GlButton,
},
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
apollo: {
canCreateSnippet: {
query() {
@@ -67,6 +71,10 @@ export default {
condition: this.snippet.userPermissions.updateSnippet,
text: __('Edit'),
href: this.editLink,
+ disabled: this.snippet.blob.binary,
+ title: this.snippet.blob.binary
+ ? __('Snippets with non-text files can only be edited via Git.')
+ : undefined,
},
{
condition: this.snippet.userPermissions.adminSnippet,
@@ -186,18 +194,24 @@ export default {
<div class="detail-page-header-actions">
<div class="d-none d-sm-flex">
<template v-for="(action, index) in personalSnippetActions">
- <gl-button
+ <div
v-if="action.condition"
:key="index"
- :disabled="action.disabled"
- :variant="action.variant"
- :category="action.category"
- :class="action.cssClass"
- :href="action.href"
- @click="action.click ? action.click() : undefined"
+ v-gl-tooltip
+ :title="action.title"
+ class="d-inline-block"
>
- {{ action.text }}
- </gl-button>
+ <gl-button
+ :disabled="action.disabled"
+ :variant="action.variant"
+ :category="action.category"
+ :class="action.cssClass"
+ :href="action.href"
+ @click="action.click ? action.click() : undefined"
+ >
+ {{ action.text }}
+ </gl-button>
+ </div>
</template>
</div>
<div class="d-block d-sm-none dropdown">
@@ -205,6 +219,8 @@ export default {
<gl-dropdown-item
v-for="(action, index) in personalSnippetActions"
:key="index"
+ :disabled="action.disabled"
+ :title="action.title"
:href="action.href"
@click="action.click ? action.click() : undefined"
>{{ action.text }}</gl-dropdown-item
diff --git a/app/assets/stylesheets/page_bundles/_ide_mixins.scss b/app/assets/stylesheets/page_bundles/_ide_mixins.scss
index 9465dd5bed6..48b8a7230b1 100644
--- a/app/assets/stylesheets/page_bundles/_ide_mixins.scss
+++ b/app/assets/stylesheets/page_bundles/_ide_mixins.scss
@@ -9,7 +9,6 @@
top: 0;
font-size: 12px;
border-top-right-radius: $border-radius-default;
- margin-left: -$gl-padding;
.controllers {
@include build-controllers(15px, center, false, 0, inline, 0);
diff --git a/app/assets/stylesheets/page_bundles/ide.scss b/app/assets/stylesheets/page_bundles/ide.scss
index c5869880af9..d0660422f7e 100644
--- a/app/assets/stylesheets/page_bundles/ide.scss
+++ b/app/assets/stylesheets/page_bundles/ide.scss
@@ -890,11 +890,15 @@ $ide-commit-header-height: 48px;
.multi-file-commit-panel-inner {
width: 350px;
- padding: $grid-size $gl-padding;
+ padding: $grid-size 0;
background-color: $white;
border-left: 1px solid $white-dark;
}
+ .ide-right-sidebar-jobs-detail {
+ padding-bottom: 0;
+ }
+
.ide-right-sidebar-clientside {
padding: 0;
}
@@ -915,15 +919,12 @@ $ide-commit-header-height: 48px;
margin: 0;
}
}
-
- .build-trace {
- margin-left: -$gl-padding;
- }
}
.ide-pipeline-list {
flex: 1;
overflow: auto;
+ padding: 0 $gl-padding;
}
.ide-pipeline-header {
@@ -966,6 +967,7 @@ $ide-commit-header-height: 48px;
.ide-job-header {
min-height: 60px;
+ padding: 0 $gl-padding;
}
.ide-nav-form {
diff --git a/app/assets/stylesheets/page_bundles/themes/_dark.scss b/app/assets/stylesheets/page_bundles/themes/_dark.scss
index 66b4b745532..634f18ee1bd 100644
--- a/app/assets/stylesheets/page_bundles/themes/_dark.scss
+++ b/app/assets/stylesheets/page_bundles/themes/_dark.scss
@@ -33,7 +33,7 @@
$diff-insert: rgba(155, 185, 85, 0.2);
$diff-remove: rgba(255, 0, 0, 0.2);
- a {
+ a:not(.btn) {
color: $link-color;
}
@@ -57,27 +57,46 @@
textarea,
.md-area.is-focused,
.ide-entry-dropdown-toggle,
- .nav-links:not(.quick-links) li:not(.md-header-toolbar) a:hover,
.dropdown-menu li button,
.ide-merge-request-project-path,
.dropdown-menu-selectable li a.is-active,
.dropdown-menu-inner-title,
- .dropdown-menu-inner-content {
+ .dropdown-menu-inner-content,
+ .nav-links:not(.quick-links) li:not(.md-header-toolbar) a,
+ .nav-links:not(.quick-links) li:not(.md-header-toolbar) a:hover,
+ .nav-links:not(.quick-links) li:not(.md-header-toolbar) a.active .badge.badge-pill,
+ .nav-links:not(.quick-links) li:not(.md-header-toolbar) a:hover .badge.badge-pill,
+ .badge.badge-pill,
+ .ide-navigator-button,
+ .bs-callout,
+ .ide-navigator-btn,
+ .ide-pipeline .top-bar,
+ .ide-pipeline .top-bar .controllers .controllers-buttons {
color: $text-color;
}
+ .drag-handle:hover,
+ .card-header .badge.badge-pill {
+ background-color: $dropdown-hover-background;
+ }
+
.modal-body {
color: $gl-text-color;
}
.dropdown-menu-toggle svg,
.dropdown-menu-toggle svg:hover,
- .ide-tree-header svg,
+ .ide-tree-header:not(.ide-pipeline-header) svg,
.file-row .file-row-icon svg,
- .file-row:hover .file-row-icon svg {
+ .file-row:hover .file-row-icon svg,
+ .controllers-buttons svg {
fill: $text-color;
}
+ .ide-pipeline svg {
+ --svg-status-bg: transparent;
+ }
+
.multi-file-tab-close:hover {
background-color: $input-border;
}
@@ -118,7 +137,12 @@
.ide-commit-editor-header,
.ide-file-templates,
.ide-entry-dropdown-toggle,
- .ide-staged-action-btn {
+ .ide-staged-action-btn,
+ .badge.badge-pill,
+ .card-header,
+ .bs-callout,
+ .ide-pipeline .top-bar,
+ .ide-terminal .top-bar {
background-color: $background;
}
@@ -126,6 +150,18 @@
background-color: inherit;
}
+ .bs-callout {
+ border-color: $dropdown-background;
+
+ code {
+ background-color: $dropdown-background;
+ }
+ }
+
+ .nav-links:not(.quick-links) li:not(.md-header-toolbar) a:hover {
+ border-color: $dropdown-hover-background;
+ }
+
.ide-sidebar-link:hover,
.multi-file-tabs li {
background-color: $background-hover;
@@ -144,7 +180,10 @@
.ide-sidebar-link.active::after,
.ide-right-sidebar .multi-file-commit-panel-inner,
.common-note-form .md-area,
- .ide-commit-message-field {
+ .ide-commit-message-field,
+ .card,
+ .multi-file-commit-panel-success-message,
+ .ide-preview-header {
background-color: $highlight-background;
}
@@ -163,7 +202,12 @@
.multi-file-tabs li,
.ide-status-bar,
.ide-commit-editor-header,
- .ide-file-templates {
+ .ide-file-templates,
+ .card,
+ .card-header,
+ .ide-job-item:not(:last-child),
+ .ide-terminal .top-bar,
+ .ide-pipeline .top-bar {
border-color: $border-color;
}
@@ -179,7 +223,9 @@
.multi-file-commit-form > form,
.multi-file-commit-form hr,
.ide-commit-list-container.is-first,
- .multi-file-commit-form .nav-links:not(.quick-links) {
+ .multi-file-commit-form .nav-links:not(.quick-links),
+ .ide-pipeline-list .nav-links:not(.quick-links),
+ .ide-preview-header {
border-color: $background;
}
@@ -201,7 +247,8 @@
}
}
- .nav-links li.active a {
+ .nav-links li.active a,
+ .nav-links li a.active {
border-color: $highlight-accent;
color: $text-color;
}
@@ -223,7 +270,7 @@
input[type='search'],
.filtered-search-box {
border-color: $input-border;
- background-color: $input-background;
+ background: $input-background !important;
}
input[type='text'],
@@ -252,16 +299,16 @@
background: $gray-800;
}
- .btn:not(.btn-link):hover {
+ .btn:not(.btn-link):not([disabled]):hover {
border-width: 2px;
padding: 5px 9px;
}
- .btn.btn-sm:hover {
+ .btn:not([disabled]).btn-sm:hover {
padding: 3px 9px;
}
- .btn.btn-block:hover {
+ .btn:not([disabled]).btn-block:hover {
padding: 5px 0;
}
diff --git a/app/graphql/mutations/alert_management/base.rb b/app/graphql/mutations/alert_management/base.rb
new file mode 100644
index 00000000000..eb0d2304ba3
--- /dev/null
+++ b/app/graphql/mutations/alert_management/base.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Mutations
+ module AlertManagement
+ class Base < BaseMutation
+ include Mutations::ResolvesProject
+
+ argument :project_path, GraphQL::ID_TYPE,
+ required: true,
+ description: "The project the alert to mutate is in"
+
+ argument :iid, GraphQL::STRING_TYPE,
+ required: true,
+ description: "The iid of the alert to mutate"
+
+ field :alert,
+ Types::AlertManagement::AlertType,
+ null: true,
+ description: "The alert after mutation"
+
+ authorize :update_alert_management_alerts
+
+ private
+
+ def find_object(project_path:, iid:)
+ project = resolve_project(full_path: project_path)
+
+ return unless project
+
+ resolver = Resolvers::AlertManagementAlertResolver.single.new(object: project, context: context, field: nil)
+ resolver.resolve(iid: iid)
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/alert_management/update_alert_status.rb b/app/graphql/mutations/alert_management/update_alert_status.rb
new file mode 100644
index 00000000000..ac6a5412956
--- /dev/null
+++ b/app/graphql/mutations/alert_management/update_alert_status.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Mutations
+ module AlertManagement
+ class UpdateAlertStatus < Base
+ graphql_name 'UpdateAlertStatus'
+
+ argument :status, Types::AlertManagement::StatusEnum,
+ required: true,
+ description: 'The status to set the alert'
+
+ def resolve(args)
+ alert = authorized_find!(project_path: args[:project_path], iid: args[:iid])
+
+ result = update_status(alert, args[:status])
+
+ prepare_response(result)
+ end
+
+ private
+
+ def update_status(alert, status)
+ ::AlertManagement::UpdateAlertStatusService.new(alert, status).execute
+ end
+
+ def prepare_response(result)
+ {
+ alert: result.payload[:alert],
+ errors: result.error? ? [result.message] : []
+ }
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/alert_management/alert_type.rb b/app/graphql/types/alert_management/alert_type.rb
index 5055a9caef2..69fc2718f1e 100644
--- a/app/graphql/types/alert_management/alert_type.rb
+++ b/app/graphql/types/alert_management/alert_type.rb
@@ -18,6 +18,11 @@ module Types
null: true,
description: 'Title of the alert'
+ field :description,
+ GraphQL::STRING_TYPE,
+ null: true,
+ description: 'Description of the alert'
+
field :severity,
AlertManagement::SeverityEnum,
null: true,
@@ -38,6 +43,11 @@ module Types
null: true,
description: 'Monitoring tool the alert came from'
+ field :hosts,
+ [GraphQL::STRING_TYPE],
+ null: true,
+ description: 'List of hosts the alert came from'
+
field :started_at,
Types::TimeType,
null: true,
@@ -53,6 +63,21 @@ module Types
null: true,
description: 'Number of events of this alert',
method: :events
+
+ field :details,
+ GraphQL::Types::JSON,
+ null: true,
+ description: 'Alert details'
+
+ field :created_at,
+ Types::TimeType,
+ null: true,
+ description: 'Timestamp the alert was created'
+
+ field :updated_at,
+ Types::TimeType,
+ null: true,
+ description: 'Timestamp the alert was last updated'
end
end
end
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index c9b8f235ae1..b18a3968a03 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -7,6 +7,7 @@ module Types
graphql_name 'Mutation'
mount_mutation Mutations::Admin::SidekiqQueues::DeleteJobs
+ mount_mutation Mutations::AlertManagement::UpdateAlertStatus
mount_mutation Mutations::AwardEmojis::Add
mount_mutation Mutations::AwardEmojis::Remove
mount_mutation Mutations::AwardEmojis::Toggle
diff --git a/app/models/alert_management/alert.rb b/app/models/alert_management/alert.rb
index 810ec8f5583..1263423e1c4 100644
--- a/app/models/alert_management/alert.rb
+++ b/app/models/alert_management/alert.rb
@@ -53,6 +53,12 @@ module AlertManagement
end
end
+ def details
+ details_payload = payload.except(*attributes.keys)
+
+ Gitlab::Utils::InlineHash.merge_keys(details_payload)
+ end
+
private
def hosts_length
diff --git a/app/models/ci/persistent_ref.rb b/app/models/ci/persistent_ref.rb
index 76139f5d676..91163c85a9e 100644
--- a/app/models/ci/persistent_ref.rb
+++ b/app/models/ci/persistent_ref.rb
@@ -14,16 +14,12 @@ module Ci
delegate :ref_exists?, :create_ref, :delete_refs, to: :repository
def exist?
- return unless enabled?
-
ref_exists?(path)
rescue
false
end
def create
- return unless enabled?
-
create_ref(sha, path)
rescue => e
Gitlab::ErrorTracking
@@ -31,8 +27,6 @@ module Ci
end
def delete
- return unless enabled?
-
delete_refs(path)
rescue Gitlab::Git::Repository::NoRepository
# no-op
@@ -44,11 +38,5 @@ module Ci
def path
"refs/#{Repository::REF_PIPELINES}/#{pipeline.id}"
end
-
- private
-
- def enabled?
- Feature.enabled?(:depend_on_persistent_pipeline_ref, project, default_enabled: true)
- end
end
end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 4e0bcfe0985..6aa3d791a0f 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -240,6 +240,7 @@ class ProjectPolicy < BasePolicy
enable :read_prometheus
enable :read_metrics_dashboard_annotation
enable :read_alert_management_alerts
+ enable :update_alert_management_alerts
enable :metrics_dashboard
end
diff --git a/app/presenters/ci/build_runner_presenter.rb b/app/presenters/ci/build_runner_presenter.rb
index a409a3d7160..61fcbaf691a 100644
--- a/app/presenters/ci/build_runner_presenter.rb
+++ b/app/presenters/ci/build_runner_presenter.rb
@@ -34,7 +34,6 @@ module Ci
def refspecs
specs = []
- specs << refspec_for_pipeline_ref if should_expose_merge_request_ref?
specs << refspec_for_persistent_ref if persistent_ref_exist?
if git_depth > 0
@@ -50,19 +49,6 @@ module Ci
private
- # We will stop exposing merge request refs when we fully depend on persistent refs
- # (i.e. remove `refspec_for_pipeline_ref` when we remove `depend_on_persistent_pipeline_ref` feature flag.)
- # `ci_force_exposing_merge_request_refs` is an extra feature flag that allows us to
- # forcibly expose MR refs even if the `depend_on_persistent_pipeline_ref` feature flag enabled.
- # This is useful when we see an unexpected behaviors/reports from users.
- # See https://gitlab.com/gitlab-org/gitlab/issues/35140.
- def should_expose_merge_request_ref?
- return false unless merge_request_ref?
- return true if Feature.enabled?(:ci_force_exposing_merge_request_refs, project)
-
- Feature.disabled?(:depend_on_persistent_pipeline_ref, project, default_enabled: true)
- end
-
def create_archive(artifacts)
return unless artifacts[:untracked] || artifacts[:paths]
@@ -100,10 +86,6 @@ module Ci
"+#{Gitlab::Git::TAG_REF_PREFIX}#{ref}:#{RUNNER_REMOTE_TAG_PREFIX}#{ref}"
end
- def refspec_for_pipeline_ref
- "+#{ref}:#{ref}"
- end
-
def refspec_for_persistent_ref
"+#{persistent_ref_path}:#{persistent_ref_path}"
end
diff --git a/app/services/alert_management/update_alert_status_service.rb b/app/services/alert_management/update_alert_status_service.rb
new file mode 100644
index 00000000000..73ee654874f
--- /dev/null
+++ b/app/services/alert_management/update_alert_status_service.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module AlertManagement
+ class UpdateAlertStatusService
+ def initialize(alert, status)
+ @alert = alert
+ @status = status
+ end
+
+ def execute
+ return error('Invalid status') unless AlertManagement::Alert.statuses.key?(status.to_s)
+
+ alert.status = status
+
+ if alert.save
+ success
+ else
+ error(alert.errors.full_messages.to_sentence)
+ end
+ end
+
+ private
+
+ attr_reader :alert, :status
+
+ def success
+ ServiceResponse.success(payload: { alert: alert })
+ end
+
+ def error(message)
+ ServiceResponse.error(payload: { alert: alert }, message: message)
+ end
+ end
+end
diff --git a/app/services/spam/spam_action_service.rb b/app/services/spam/spam_action_service.rb
index 0c938ba00c2..f0a4aff4443 100644
--- a/app/services/spam/spam_action_service.rb
+++ b/app/services/spam/spam_action_service.rb
@@ -28,6 +28,7 @@ module Spam
# update the spam log accordingly.
SpamLog.verify_recaptcha!(user_id: user.id, id: spam_log_id)
else
+ return if allowlisted?(user)
return unless request
return unless check_for_spam?
@@ -39,6 +40,10 @@ module Spam
private
+ def allowlisted?(user)
+ user.respond_to?(:gitlab_employee) && user.gitlab_employee?
+ end
+
def perform_spam_service_check(api)
# since we can check for spam, and recaptcha is not verified,
# ask the SpamVerdictService what to do with the target.
diff --git a/app/views/projects/alert_management/details.html.haml b/app/views/projects/alert_management/details.html.haml
index 1208c541a9d..766dbf7c128 100644
--- a/app/views/projects/alert_management/details.html.haml
+++ b/app/views/projects/alert_management/details.html.haml
@@ -1,3 +1,4 @@
-- page_title _('Alert Details')
+- add_to_breadcrumbs s_('AlertManagement|Alerts'), project_alert_management_index_path(@project)
+- page_title s_('AlertManagement|Alert detail')
#js-alert_details
diff --git a/changelogs/unreleased/212213-cablett-allowlist-gitlab-team-member.yml b/changelogs/unreleased/212213-cablett-allowlist-gitlab-team-member.yml
new file mode 100644
index 00000000000..5f812369a74
--- /dev/null
+++ b/changelogs/unreleased/212213-cablett-allowlist-gitlab-team-member.yml
@@ -0,0 +1,5 @@
+---
+title: Skip spam check for GitLab team members on gitlab.com
+merge_request: 31052
+author:
+type: added
diff --git a/changelogs/unreleased/212592-disable-binary-edit.yml b/changelogs/unreleased/212592-disable-binary-edit.yml
new file mode 100644
index 00000000000..e115d75108e
--- /dev/null
+++ b/changelogs/unreleased/212592-disable-binary-edit.yml
@@ -0,0 +1,5 @@
+---
+title: Disabled Edit button for binary snippets
+merge_request: 30904
+author:
+type: added
diff --git a/changelogs/unreleased/214542-graphql-status-mutation.yml b/changelogs/unreleased/214542-graphql-status-mutation.yml
new file mode 100644
index 00000000000..4a8b4cca37a
--- /dev/null
+++ b/changelogs/unreleased/214542-graphql-status-mutation.yml
@@ -0,0 +1,5 @@
+---
+title: Add mutation for AlertManagement's Alert status
+merge_request: 30576
+author:
+type: added
diff --git a/changelogs/unreleased/expose-more-fields-in-alert-management-alert-graphql.yml b/changelogs/unreleased/expose-more-fields-in-alert-management-alert-graphql.yml
new file mode 100644
index 00000000000..7003180bb32
--- /dev/null
+++ b/changelogs/unreleased/expose-more-fields-in-alert-management-alert-graphql.yml
@@ -0,0 +1,5 @@
+---
+title: Exposes description, hosts, details, and timestamps for Alert Management Alert GraphQL
+merge_request: 31091
+author:
+type: changed
diff --git a/changelogs/unreleased/kubeclient-create-or-update.yml b/changelogs/unreleased/kubeclient-create-or-update.yml
new file mode 100644
index 00000000000..143738c5195
--- /dev/null
+++ b/changelogs/unreleased/kubeclient-create-or-update.yml
@@ -0,0 +1,5 @@
+---
+title: Uses Kubernetes API conventions to create or update a resource leandrogs
+merge_request: 29010
+author: Leandro Silva
+type: performance
diff --git a/changelogs/unreleased/remove-sidekiq-rake-tasks.yml b/changelogs/unreleased/remove-sidekiq-rake-tasks.yml
new file mode 100644
index 00000000000..f2677b18146
--- /dev/null
+++ b/changelogs/unreleased/remove-sidekiq-rake-tasks.yml
@@ -0,0 +1,5 @@
+---
+title: Remove deprecated Sidekiq rake tasks
+merge_request:
+author:
+type: removed
diff --git a/doc/administration/reference_architectures/index.md b/doc/administration/reference_architectures/index.md
index 88a4f2c97a6..79d4a3328b1 100644
--- a/doc/administration/reference_architectures/index.md
+++ b/doc/administration/reference_architectures/index.md
@@ -62,11 +62,20 @@ This solution is appropriate for many teams that have a single server at their d
You can also optionally configure GitLab to use an [external PostgreSQL service](../external_database.md) or an [external object storage service](../high_availability/object_storage.md) for added performance and reliability at a relatively low complexity cost.
-<!--
## Up to 2,000 users
-For up to 2,000 users, defining the reference architecture is [being worked on](https://gitlab.com/gitlab-org/quality/performance/-/issues/223).
--->
+> - **Supported users (approximate):** 2,000
+> - **High Availability:** False
+> - **Test RPS rates:** API: 40 RPS, Web: 4 RPS, Git: 4 RPS
+
+| Service | Nodes | Configuration ([8](#footnotes)) | GCP type | AWS type ([9](#footnotes)) |
+|--------------------------------------------------------------|-------|---------------------------------|---------------|----------------------------|
+| GitLab Rails, Sidekiq, Consul ([1](#footnotes)) | 2 | 8 vCPU, 7.2GB Memory | n1-highcpu-8 | c5.2xlarge |
+| PostgreSQL | 1 | 2 vCPU, 7.5GB Memory | n1-standard-2 | m5.large |
+| Gitaly ([2](#footnotes)) ([5](#footnotes)) ([7](#footnotes)) | X | 4 vCPU, 15GB Memory | n1-standard-4 | m5.xlarge |
+| Cloud Object Storage ([4](#footnotes)) | - | - | - | - |
+| NFS Server ([5](#footnotes)) ([7](#footnotes)) | 1 | 4 vCPU, 3.6GB Memory | n1-highcpu-4 | c5.xlarge |
+| External load balancing node ([6](#footnotes)) | 1 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 | c5.large |
## Up to 3,000 users
@@ -79,7 +88,8 @@ server, a PostgreSQL server and a Redis server. A reference architecture with
this alternative in mind is [being worked on](https://gitlab.com/gitlab-org/quality/performance/-/issues/223).
> - **Supported users (approximate):** 3,000
-> - **Test RPS rates:** API: 40 RPS, Web: 4 RPS, Git: 4 RPS
+> - **High Availability:** True
+> - **Test RPS rates:** API: 60 RPS, Web: 6 RPS, Git: 6 RPS
| Service | Nodes | Configuration ([8](#footnotes)) | GCP type | AWS type ([9](#footnotes)) |
|--------------------------------------------------------------|-------|---------------------------------|---------------|----------------------------|
@@ -99,6 +109,7 @@ this alternative in mind is [being worked on](https://gitlab.com/gitlab-org/qual
## Up to 5,000 users
> - **Supported users (approximate):** 5,000
+> - **High Availability:** True
> - **Test RPS rates:** API: 100 RPS, Web: 10 RPS, Git: 10 RPS
| Service | Nodes | Configuration ([8](#footnotes)) | GCP type | AWS type ([9](#footnotes)) |
@@ -119,6 +130,7 @@ this alternative in mind is [being worked on](https://gitlab.com/gitlab-org/qual
## Up to 10,000 users
> - **Supported users (approximate):** 10,000
+> - **High Availability:** True
> - **Test RPS rates:** API: 200 RPS, Web: 20 RPS, Git: 20 RPS
| Service | Nodes | GCP Configuration ([8](#footnotes)) | GCP type | AWS type ([9](#footnotes)) |
@@ -142,6 +154,7 @@ this alternative in mind is [being worked on](https://gitlab.com/gitlab-org/qual
## Up to 25,000 users
> - **Supported users (approximate):** 25,000
+> - **High Availability:** True
> - **Test RPS rates:** API: 500 RPS, Web: 50 RPS, Git: 50 RPS
| Service | Nodes | Configuration ([8](#footnotes)) | GCP type | AWS type ([9](#footnotes)) |
@@ -165,6 +178,7 @@ this alternative in mind is [being worked on](https://gitlab.com/gitlab-org/qual
## Up to 50,000 users
> - **Supported users (approximate):** 50,000
+> - **High Availability:** True
> - **Test RPS rates:** API: 1000 RPS, Web: 100 RPS, Git: 100 RPS
| Service | Nodes | Configuration ([8](#footnotes)) | GCP type | AWS type ([9](#footnotes)) |
@@ -288,7 +302,10 @@ column.
## Footnotes
1. In our architectures we run each GitLab Rails node using the Puma webserver
- and have its number of workers set to 90% of available CPUs along with four threads.
+ and have its number of workers set to 90% of available CPUs along with four threads. For
+ nodes that are running Rails with other components the worker value should be reduced
+ accordingly where we've found 50% achieves a good balance but this is dependent
+ on workload.
1. Gitaly node requirements are dependent on customer data, specifically the number of
projects and their sizes. We recommend two nodes as an absolute minimum for HA environments
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index b4cfd50422a..69ca80015a7 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -143,6 +143,21 @@ Describes an alert from the project's Alert Management
"""
type AlertManagementAlert {
"""
+ Timestamp the alert was created
+ """
+ createdAt: Time
+
+ """
+ Description of the alert
+ """
+ description: String
+
+ """
+ Alert details
+ """
+ details: JSON
+
+ """
Timestamp the alert ended
"""
endedAt: Time
@@ -153,6 +168,11 @@ type AlertManagementAlert {
eventCount: Int
"""
+ List of hosts the alert came from
+ """
+ hosts: [String!]
+
+ """
Internal ID of the alert
"""
iid: ID!
@@ -186,6 +206,11 @@ type AlertManagementAlert {
Title of the alert
"""
title: String
+
+ """
+ Timestamp the alert was last updated
+ """
+ updatedAt: Time
}
"""
@@ -6074,6 +6099,7 @@ type Mutation {
todoRestoreMany(input: TodoRestoreManyInput!): TodoRestoreManyPayload
todosMarkAllDone(input: TodosMarkAllDoneInput!): TodosMarkAllDonePayload
toggleAwardEmoji(input: ToggleAwardEmojiInput!): ToggleAwardEmojiPayload
+ updateAlertStatus(input: UpdateAlertStatusInput!): UpdateAlertStatusPayload
updateEpic(input: UpdateEpicInput!): UpdateEpicPayload
"""
@@ -9729,6 +9755,51 @@ enum TypeEnum {
project
}
+"""
+Autogenerated input type of UpdateAlertStatus
+"""
+input UpdateAlertStatusInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The iid of the alert to mutate
+ """
+ iid: String!
+
+ """
+ The project the alert to mutate is in
+ """
+ projectPath: ID!
+
+ """
+ The status to set the alert
+ """
+ status: AlertManagementStatus!
+}
+
+"""
+Autogenerated return type of UpdateAlertStatus
+"""
+type UpdateAlertStatusPayload {
+ """
+ The alert after mutation
+ """
+ alert: AlertManagementAlert
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Reasons why the mutation failed.
+ """
+ errors: [String!]!
+}
+
input UpdateDiffImagePositionInput {
"""
Total height of the image
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index 7a4bc3f6360..4ea50ac353e 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -395,6 +395,48 @@
"description": "Describes an alert from the project's Alert Management",
"fields": [
{
+ "name": "createdAt",
+ "description": "Timestamp the alert was created",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Time",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "description",
+ "description": "Description of the alert",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "details",
+ "description": "Alert details",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "JSON",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "endedAt",
"description": "Timestamp the alert ended",
"args": [
@@ -423,6 +465,28 @@
"deprecationReason": null
},
{
+ "name": "hosts",
+ "description": "List of hosts the alert came from",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "iid",
"description": "Internal ID of the alert",
"args": [
@@ -523,6 +587,20 @@
},
"isDeprecated": false,
"deprecationReason": null
+ },
+ {
+ "name": "updatedAt",
+ "description": "Timestamp the alert was last updated",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Time",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
}
],
"inputFields": null,
@@ -18130,6 +18208,33 @@
"deprecationReason": null
},
{
+ "name": "updateAlertStatus",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "UpdateAlertStatusInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "UpdateAlertStatusPayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "updateEpic",
"description": null,
"args": [
@@ -29250,6 +29355,136 @@
},
{
"kind": "INPUT_OBJECT",
+ "name": "UpdateAlertStatusInput",
+ "description": "Autogenerated input type of UpdateAlertStatus",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "projectPath",
+ "description": "The project the alert to mutate is in",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "iid",
+ "description": "The iid of the alert to mutate",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "status",
+ "description": "The status to set the alert",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "ENUM",
+ "name": "AlertManagementStatus",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "UpdateAlertStatusPayload",
+ "description": "Autogenerated return type of UpdateAlertStatus",
+ "fields": [
+ {
+ "name": "alert",
+ "description": "The alert after mutation",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "AlertManagementAlert",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Reasons why the mutation failed.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "INPUT_OBJECT",
"name": "UpdateDiffImagePositionInput",
"description": null,
"fields": null,
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 8b47fbda02e..185df0c2fe4 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -52,8 +52,12 @@ Describes an alert from the project's Alert Management
| Name | Type | Description |
| --- | ---- | ---------- |
+| `createdAt` | Time | Timestamp the alert was created |
+| `description` | String | Description of the alert |
+| `details` | JSON | Alert details |
| `endedAt` | Time | Timestamp the alert ended |
| `eventCount` | Int | Number of events of this alert |
+| `hosts` | String! => Array | List of hosts the alert came from |
| `iid` | ID! | Internal ID of the alert |
| `monitoringTool` | String | Monitoring tool the alert came from |
| `service` | String | Service the alert came from |
@@ -61,6 +65,7 @@ Describes an alert from the project's Alert Management
| `startedAt` | Time | Timestamp the alert was raised |
| `status` | AlertManagementStatus | Status of the alert |
| `title` | String | Title of the alert |
+| `updatedAt` | Time | Timestamp the alert was last updated |
## AwardEmoji
@@ -1515,6 +1520,16 @@ Represents a directory
| `type` | EntryType! | Type of tree entry |
| `webUrl` | String | Web URL for the tree entry (directory) |
+## UpdateAlertStatusPayload
+
+Autogenerated return type of UpdateAlertStatus
+
+| Name | Type | Description |
+| --- | ---- | ---------- |
+| `alert` | AlertManagementAlert | The alert after mutation |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `errors` | String! => Array | Reasons why the mutation failed. |
+
## UpdateEpicPayload
Autogenerated return type of UpdateEpic
diff --git a/doc/ci/pipelines/index.md b/doc/ci/pipelines/index.md
index d2632dfe719..d4fa41ca19e 100644
--- a/doc/ci/pipelines/index.md
+++ b/doc/ci/pipelines/index.md
@@ -549,15 +549,3 @@ To illustrate its life cycle:
even if the commit history of the `example` branch has been overwritten by force-push.
1. GitLab Runner fetches the persistent pipeline ref and gets source code from the checkout-SHA.
1. When the pipeline finished, its persistent ref is cleaned up in a background process.
-
-NOTE: **NOTE**: At this moment, this feature is on by default and can be manually disabled
-by disabling `depend_on_persistent_pipeline_ref` feature flag. If you're interested in
-manually disabling this behavior, please ask the administrator
-to execute the following commands in rails console.
-
-```shell
-> sudo gitlab-rails console # Login to Rails console of GitLab instance.
-> project = Project.find_by_full_path('namespace/project-name') # Get the project instance.
-> Feature.disable(:depend_on_persistent_pipeline_ref, project) # Disable the feature flag for specific project
-> Feature.disable(:depend_on_persistent_pipeline_ref) # Disable the feature flag system-wide
-```
diff --git a/doc/install/aws/index.md b/doc/install/aws/index.md
index 8daa9b40111..05906520c1c 100644
--- a/doc/install/aws/index.md
+++ b/doc/install/aws/index.md
@@ -63,17 +63,52 @@ Here's a list of the AWS services we will use, with links to pricing information
NOTE: **Note:** Please note that while we will be using EBS for storage, we do not recommend using EFS as it may negatively impact GitLab's performance. You can review the [relevant documentation](../../administration/high_availability/nfs.md#avoid-using-awss-elastic-file-system-efs) for more details.
-## Creating an IAM EC2 instance role and profile
+## Create an IAM EC2 instance role and profile
+
+As we'll be using [Amazon S3 object storage](#amazon-s3-object-storage), our EC2 instances need to have read, write, and list permissions for our S3 buckets. To avoid embedding AWS keys in our GitLab config, we'll make use of an [IAM Role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html) to allow our GitLab instance with this access. We'll need to create an IAM policy to attach to our IAM role:
+
+### Create an IAM Policy
+
+1. Navigate to the IAM dashboard and click on **Policies** in the left menu.
+1. Click **Create policy**, select the `JSON` tab, and add a policy. We want to [follow security best practices and grant _least privilege_](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege), giving our role only the permissions needed to perform the required actions.
+ 1. Assuming you prefix the S3 bucket names with `gl-` as shown in the diagram, add the following policy:
+
+```json
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": [
+ "s3:AbortMultipartUpload",
+ "s3::CompleteMultipartUpload",
+ "s3:ListBucket",
+ "s3:PutObject",
+ "s3:GetObject",
+ "s3:DeleteObject",
+ "s3:PutObjectAcl"
+ ],
+ "Resource": [
+ "arn:aws:s3:::gl-*/*"
+ ]
+ }
+ ]
+}
+```
+
+1. Click **Review policy**, give your policy a name (we'll use `gl-s3-policy`), and click **Create policy**.
-To minimize the permissions of the user, we'll create a new [IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html)
-role with limited access:
+### Create an IAM Role
-1. Navigate to the IAM dashboard <https://console.aws.amazon.com/iam/home>, click on **Roles** in the left menu, and
+1. Still on the IAM dashboard, click on **Roles** in the left menu, and
click **Create role**.
1. Create a new role by selecting **AWS service > EC2**, then click
**Next: Permissions**.
-1. Choose **AmazonEC2FullAccess** and **AmazonS3FullAccess**, click **Tags** and add tags if needed.
-1. Click **Review**, give your role the name (we'll use `GitLabAdmin`), and click **Create role**.
+1. In the policy filter, search for the `gl-s3-policy` we created above, select it, and click **Tags**.
+1. Add tags if needed and click **Review**.
+1. Give the role a name (we'll use `GitLabS3Access`) and click **Create Role**.
+
+We'll use this role when we [create a launch configuration](#create-a-launch-configuration) later on.
## Configuring the network
@@ -575,7 +610,10 @@ HostKey /etc/ssh_static/ssh_host_ed25519_key
#### Amazon S3 object storage
-Since we're not using NFS for shared storage, we will use [Amazon S3](https://aws.amazon.com/s3/) buckets to store backups, artifacts, LFS objects, uploads, merge request diffs, container registry images, and more. Our [documentation includes configuration instructions](../../administration/object_storage.md) for each of these, and other information about using object storage with GitLab.
+Since we're not using NFS for shared storage, we will use [Amazon S3](https://aws.amazon.com/s3/) buckets to store backups, artifacts, LFS objects, uploads, merge request diffs, container registry images, and more. Our documentation includes [instructions on how to configure object storage](../../administration/object_storage.md) for each of these data types, and other information about using object storage with GitLab.
+
+NOTE: **Note:**
+Since we are using the [AWS IAM profile](#create-an-iam-role) we created earlier, be sure to omit the AWS access key and secret access key/value pairs when configuring object storage. Instead, use `'use_iam_profile' => true` in your configuration as shown in the object storage documentation linked above.
Remember to run `sudo gitlab-ctl reconfigure` after saving the changes to the `gitlab.rb` file.
@@ -611,7 +649,7 @@ From the EC2 dashboard:
1. Select an instance type best suited for your needs (at least a `c5.xlarge`) and click **Configure details**.
1. Enter a name for your launch configuration (we'll use `gitlab-ha-launch-config`).
1. **Do not** check **Request Spot Instance**.
-1. From the **IAM Role** dropdown, pick the `GitLabAdmin` instance role we [created earlier](#creating-an-iam-ec2-instance-role-and-profile).
+1. From the **IAM Role** dropdown, pick the `GitLabAdmin` instance role we [created earlier](#create-an-iam-ec2-instance-role-and-profile).
1. Leave the rest as defaults and click **Add Storage**.
1. The root volume is 8GiB by default and should be enough given that we won’t store any data there. Click **Configure Security Group**.
1. Check **Select and existing security group** and select the `gitlab-loadbalancer-sec-group` we created earlier.
diff --git a/lib/gitlab/graphql/authorize/authorize_field_service.rb b/lib/gitlab/graphql/authorize/authorize_field_service.rb
index c7f430490d6..61668b634fd 100644
--- a/lib/gitlab/graphql/authorize/authorize_field_service.rb
+++ b/lib/gitlab/graphql/authorize/authorize_field_service.rb
@@ -70,7 +70,10 @@ module Gitlab
end
def filter_allowed(current_user, resolved_type, authorizing_object)
- if authorizing_object
+ if resolved_type.nil?
+ # We're not rendering anything, for example when a record was not found
+ # no need to do anything
+ elsif authorizing_object
# Authorizing fields representing scalars, or a simple field with an object
resolved_type if allowed_access?(current_user, authorizing_object)
elsif @field.connection?
@@ -83,9 +86,6 @@ module Gitlab
resolved_type.select do |single_object_type|
allowed_access?(current_user, single_object_type.object)
end
- elsif resolved_type.nil?
- # We're not rendering anything, for example when a record was not found
- # no need to do anything
else
raise "Can't authorize #{@field}"
end
diff --git a/lib/gitlab/kubernetes/helm/api.rb b/lib/gitlab/kubernetes/helm/api.rb
index 3b843799d66..ceda18442d6 100644
--- a/lib/gitlab/kubernetes/helm/api.rb
+++ b/lib/gitlab/kubernetes/helm/api.rb
@@ -99,11 +99,7 @@ module Gitlab
command.cluster_role_binding_resource.tap do |cluster_role_binding_resource|
break unless cluster_role_binding_resource
- if cluster_role_binding_exists?(cluster_role_binding_resource)
- kubeclient.update_cluster_role_binding(cluster_role_binding_resource)
- else
- kubeclient.create_cluster_role_binding(cluster_role_binding_resource)
- end
+ kubeclient.update_cluster_role_binding(cluster_role_binding_resource)
end
end
diff --git a/lib/gitlab/kubernetes/kube_client.rb b/lib/gitlab/kubernetes/kube_client.rb
index 2151e50a80d..7a2f2db7cb0 100644
--- a/lib/gitlab/kubernetes/kube_client.rb
+++ b/lib/gitlab/kubernetes/kube_client.rb
@@ -57,9 +57,7 @@ module Gitlab
# RBAC methods delegates to the apis/rbac.authorization.k8s.io api
# group client
- delegate :create_cluster_role_binding,
- :get_cluster_role_binding,
- :update_cluster_role_binding,
+ delegate :update_cluster_role_binding,
to: :rbac_client
# RBAC methods delegates to the apis/rbac.authorization.k8s.io api
@@ -71,9 +69,7 @@ module Gitlab
# RBAC methods delegates to the apis/rbac.authorization.k8s.io api
# group client
- delegate :create_role_binding,
- :get_role_binding,
- :update_role_binding,
+ delegate :update_role_binding,
to: :rbac_client
# non-entity methods that can only work with the core client
@@ -134,19 +130,11 @@ module Gitlab
end
def create_or_update_cluster_role_binding(resource)
- if cluster_role_binding_exists?(resource)
- update_cluster_role_binding(resource)
- else
- create_cluster_role_binding(resource)
- end
+ update_cluster_role_binding(resource)
end
def create_or_update_role_binding(resource)
- if role_binding_exists?(resource)
- update_role_binding(resource)
- else
- create_role_binding(resource)
- end
+ update_role_binding(resource)
end
def create_or_update_service_account(resource)
@@ -173,18 +161,6 @@ module Gitlab
Gitlab::UrlBlocker.validate!(api_prefix, allow_local_network: false)
end
- def cluster_role_binding_exists?(resource)
- get_cluster_role_binding(resource.metadata.name)
- rescue ::Kubeclient::ResourceNotFoundError
- false
- end
-
- def role_binding_exists?(resource)
- get_role_binding(resource.metadata.name, resource.metadata.namespace)
- rescue ::Kubeclient::ResourceNotFoundError
- false
- end
-
def service_account_exists?(resource)
get_service_account(resource.metadata.name, resource.metadata.namespace)
rescue ::Kubeclient::ResourceNotFoundError
diff --git a/lib/gitlab/sidekiq_config/cli_methods.rb b/lib/gitlab/sidekiq_config/cli_methods.rb
index c49432f0fc6..0d0efe8ffbd 100644
--- a/lib/gitlab/sidekiq_config/cli_methods.rb
+++ b/lib/gitlab/sidekiq_config/cli_methods.rb
@@ -48,7 +48,6 @@ module Gitlab
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def worker_queues(rails_path = Rails.root.to_s)
- # https://gitlab.com/gitlab-org/gitlab/issues/199230
worker_names(all_queues(rails_path))
end
@@ -75,7 +74,7 @@ module Gitlab
private
def worker_names(workers)
- workers.map { |queue| queue.is_a?(Hash) ? queue[:name] : queue }
+ workers.map { |queue| queue[:name] }
end
def query_string_to_lambda(query_string)
diff --git a/lib/tasks/sidekiq.rake b/lib/tasks/sidekiq.rake
deleted file mode 100644
index d74878835fd..00000000000
--- a/lib/tasks/sidekiq.rake
+++ /dev/null
@@ -1,38 +0,0 @@
-namespace :sidekiq do
- def deprecation_warning!
- warn <<~WARNING
- This task is deprecated and will be removed in 13.0 as it is thought to be unused.
-
- If you are using this task, please comment on the below issue:
- https://gitlab.com/gitlab-org/gitlab/issues/196731
- WARNING
- end
-
- desc '[DEPRECATED] GitLab | Sidekiq | Stop sidekiq'
- task :stop do
- deprecation_warning!
-
- system(*%w(bin/background_jobs stop))
- end
-
- desc '[DEPRECATED] GitLab | Sidekiq | Start sidekiq'
- task :start do
- deprecation_warning!
-
- system(*%w(bin/background_jobs start))
- end
-
- desc '[DEPRECATED] GitLab | Sidekiq | Restart sidekiq'
- task :restart do
- deprecation_warning!
-
- system(*%w(bin/background_jobs restart))
- end
-
- desc '[DEPRECATED] GitLab | Sidekiq | Start sidekiq with launchd on Mac OS X'
- task :launchd do
- deprecation_warning!
-
- system(*%w(bin/background_jobs start_silent))
- end
-end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 04d92df901e..f1c6e1569dd 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1697,15 +1697,18 @@ msgid_plural "Alerts"
msgstr[0] ""
msgstr[1] ""
-msgid "Alert Details"
-msgstr ""
-
msgid "AlertManagement|Acknowledged"
msgstr ""
msgid "AlertManagement|Alert"
msgstr ""
+msgid "AlertManagement|Alert detail"
+msgstr ""
+
+msgid "AlertManagement|Alerts"
+msgstr ""
+
msgid "AlertManagement|Authorize external service"
msgstr ""
@@ -19264,6 +19267,9 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "Snippets with non-text files can only be edited via Git."
+msgstr ""
+
msgid "SnippetsEmptyState|Code snippets"
msgstr ""
diff --git a/package.json b/package.json
index b5b772601eb..dd03299964b 100644
--- a/package.json
+++ b/package.json
@@ -40,7 +40,7 @@
"@babel/preset-env": "^7.8.4",
"@gitlab/at.js": "1.5.5",
"@gitlab/svgs": "1.125.0",
- "@gitlab/ui": "13.9.0",
+ "@gitlab/ui": "14.0.0",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "^6.0.2-2",
"@sentry/browser": "^5.10.2",
diff --git a/spec/frontend/registry/explorer/pages/details_spec.js b/spec/frontend/registry/explorer/pages/details_spec.js
index d6741aa3141..0c579db52ea 100644
--- a/spec/frontend/registry/explorer/pages/details_spec.js
+++ b/spec/frontend/registry/explorer/pages/details_spec.js
@@ -4,7 +4,12 @@ import Tracking from '~/tracking';
import stubChildren from 'helpers/stub_children';
import component from '~/registry/explorer/pages/details.vue';
import { createStore } from '~/registry/explorer/stores/';
-import { SET_MAIN_LOADING, SET_INITIAL_STATE } from '~/registry/explorer/stores/mutation_types/';
+import {
+ SET_MAIN_LOADING,
+ SET_INITIAL_STATE,
+ SET_TAGS_LIST_SUCCESS,
+ SET_TAGS_PAGINATION,
+} from '~/registry/explorer/stores/mutation_types/';
import {
DELETE_TAG_SUCCESS_MESSAGE,
DELETE_TAG_ERROR_MESSAGE,
@@ -60,7 +65,9 @@ describe('Details Page', () => {
beforeEach(() => {
store = createStore();
dispatchSpy = jest.spyOn(store, 'dispatch');
- store.dispatch('receiveTagsListSuccess', tagsListResponse);
+ dispatchSpy.mockResolvedValue();
+ store.commit(SET_TAGS_LIST_SUCCESS, tagsListResponse.data);
+ store.commit(SET_TAGS_PAGINATION, tagsListResponse.headers);
jest.spyOn(Tracking, 'event');
});
diff --git a/spec/frontend/registry/explorer/pages/list_spec.js b/spec/frontend/registry/explorer/pages/list_spec.js
index f69b849521d..1d530483093 100644
--- a/spec/frontend/registry/explorer/pages/list_spec.js
+++ b/spec/frontend/registry/explorer/pages/list_spec.js
@@ -1,5 +1,4 @@
-import VueRouter from 'vue-router';
-import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { shallowMount } from '@vue/test-utils';
import { GlPagination, GlSkeletonLoader, GlSprintf, GlAlert } from '@gitlab/ui';
import Tracking from '~/tracking';
import component from '~/registry/explorer/pages/list.vue';
@@ -7,22 +6,25 @@ import QuickstartDropdown from '~/registry/explorer/components/quickstart_dropdo
import GroupEmptyState from '~/registry/explorer/components/group_empty_state.vue';
import ProjectEmptyState from '~/registry/explorer/components/project_empty_state.vue';
import ProjectPolicyAlert from '~/registry/explorer/components/project_policy_alert.vue';
-import store from '~/registry/explorer/stores/';
-import { SET_MAIN_LOADING } from '~/registry/explorer/stores/mutation_types/';
+import { createStore } from '~/registry/explorer/stores/';
+import {
+ SET_MAIN_LOADING,
+ SET_IMAGES_LIST_SUCCESS,
+ SET_PAGINATION,
+ SET_INITIAL_STATE,
+} from '~/registry/explorer/stores/mutation_types/';
import {
DELETE_IMAGE_SUCCESS_MESSAGE,
DELETE_IMAGE_ERROR_MESSAGE,
} from '~/registry/explorer/constants';
import { imagesListResponse } from '../mock_data';
-import { GlModal, GlEmptyState } from '../stubs';
+import { GlModal, GlEmptyState, RouterLink } from '../stubs';
import { $toast } from '../../shared/mocks';
-const localVue = createLocalVue();
-localVue.use(VueRouter);
-
describe('List Page', () => {
let wrapper;
let dispatchSpy;
+ let store;
const findDeleteBtn = () => wrapper.find({ ref: 'deleteImageButton' });
const findDeleteModal = () => wrapper.find(GlModal);
@@ -39,21 +41,31 @@ describe('List Page', () => {
const findProjectPolicyAlert = () => wrapper.find(ProjectPolicyAlert);
const findDeleteAlert = () => wrapper.find(GlAlert);
- beforeEach(() => {
+ const mountComponent = ({ mocks } = {}) => {
wrapper = shallowMount(component, {
- localVue,
store,
stubs: {
GlModal,
GlEmptyState,
GlSprintf,
+ RouterLink,
},
mocks: {
$toast,
+ $route: {
+ name: 'foo',
+ },
+ ...mocks,
},
});
+ };
+
+ beforeEach(() => {
+ store = createStore();
dispatchSpy = jest.spyOn(store, 'dispatch');
- store.dispatch('receiveImagesListSuccess', imagesListResponse);
+ dispatchSpy.mockResolvedValue();
+ store.commit(SET_IMAGES_LIST_SUCCESS, imagesListResponse.data);
+ store.commit(SET_PAGINATION, imagesListResponse.headers);
});
afterEach(() => {
@@ -61,17 +73,38 @@ describe('List Page', () => {
});
describe('Expiration policy notification', () => {
+ beforeEach(() => {
+ mountComponent();
+ });
it('shows up on project page', () => {
expect(findProjectPolicyAlert().exists()).toBe(true);
});
it('does show up on group page', () => {
- store.dispatch('setInitialState', { isGroupPage: true });
+ store.commit(SET_INITIAL_STATE, { isGroupPage: true });
return wrapper.vm.$nextTick().then(() => {
expect(findProjectPolicyAlert().exists()).toBe(false);
});
});
});
+ describe('API calls', () => {
+ it.each`
+ imageList | name | called
+ ${[]} | ${'foo'} | ${['requestImagesList']}
+ ${imagesListResponse.data} | ${undefined} | ${['requestImagesList']}
+ ${imagesListResponse.data} | ${'foo'} | ${undefined}
+ `(
+ 'with images equal $imageList and name $name dispatch calls $called',
+ ({ imageList, name, called }) => {
+ store.commit(SET_IMAGES_LIST_SUCCESS, imageList);
+ dispatchSpy.mockClear();
+ mountComponent({ mocks: { $route: { name } } });
+
+ expect(dispatchSpy.mock.calls[0]).toEqual(called);
+ },
+ );
+ });
+
describe('connection error', () => {
const config = {
characterError: true,
@@ -79,12 +112,13 @@ describe('List Page', () => {
helpPagePath: 'bar',
};
- beforeAll(() => {
- store.dispatch('setInitialState', config);
+ beforeEach(() => {
+ store.commit(SET_INITIAL_STATE, config);
+ mountComponent();
});
- afterAll(() => {
- store.dispatch('setInitialState', {});
+ afterEach(() => {
+ store.commit(SET_INITIAL_STATE, {});
});
it('should show an empty state', () => {
@@ -106,9 +140,12 @@ describe('List Page', () => {
});
describe('isLoading is true', () => {
- beforeAll(() => store.commit(SET_MAIN_LOADING, true));
+ beforeEach(() => {
+ store.commit(SET_MAIN_LOADING, true);
+ mountComponent();
+ });
- afterAll(() => store.commit(SET_MAIN_LOADING, false));
+ afterEach(() => store.commit(SET_MAIN_LOADING, false));
it('shows the skeleton loader', () => {
expect(findSkeletonLoader().exists()).toBe(true);
@@ -125,7 +162,8 @@ describe('List Page', () => {
describe('list is empty', () => {
beforeEach(() => {
- store.dispatch('receiveImagesListSuccess', { data: [] });
+ store.commit(SET_IMAGES_LIST_SUCCESS, []);
+ mountComponent();
});
it('quick start is not visible', () => {
@@ -137,12 +175,13 @@ describe('List Page', () => {
});
describe('is group page is true', () => {
- beforeAll(() => {
- store.dispatch('setInitialState', { isGroupPage: true });
+ beforeEach(() => {
+ store.commit(SET_INITIAL_STATE, { isGroupPage: true });
+ mountComponent();
});
- afterAll(() => {
- store.dispatch('setInitialState', { isGroupPage: undefined });
+ afterEach(() => {
+ store.commit(SET_INITIAL_STATE, { isGroupPage: undefined });
});
it('group empty state is visible', () => {
@@ -156,6 +195,10 @@ describe('List Page', () => {
});
describe('list is not empty', () => {
+ beforeEach(() => {
+ mountComponent();
+ });
+
it('quick start is visible', () => {
expect(findQuickStartDropdown().exists()).toBe(true);
});
diff --git a/spec/frontend/registry/explorer/stubs.js b/spec/frontend/registry/explorer/stubs.js
index 2c2c7587af9..0e178abfbed 100644
--- a/spec/frontend/registry/explorer/stubs.js
+++ b/spec/frontend/registry/explorer/stubs.js
@@ -9,3 +9,8 @@ export const GlEmptyState = {
template: '<div><slot name="description"></slot></div>',
name: 'GlEmptyStateSTub',
};
+
+export const RouterLink = {
+ template: `<div><slot></slot></div>`,
+ props: ['to'],
+};
diff --git a/spec/frontend/snippets/components/snippet_header_spec.js b/spec/frontend/snippets/components/snippet_header_spec.js
index 16a66c70d6a..fb04959a7bf 100644
--- a/spec/frontend/snippets/components/snippet_header_spec.js
+++ b/spec/frontend/snippets/components/snippet_header_spec.js
@@ -7,26 +7,27 @@ import { shallowMount } from '@vue/test-utils';
describe('Snippet header component', () => {
let wrapper;
const snippet = {
- snippet: {
- id: 'gid://gitlab/PersonalSnippet/50',
- title: 'The property of Thor',
- visibilityLevel: 'private',
- webUrl: 'http://personal.dev.null/42',
- userPermissions: {
- adminSnippet: true,
- updateSnippet: true,
- reportSnippet: false,
- },
- project: null,
- author: {
- name: 'Thor Odinson',
- },
+ id: 'gid://gitlab/PersonalSnippet/50',
+ title: 'The property of Thor',
+ visibilityLevel: 'private',
+ webUrl: 'http://personal.dev.null/42',
+ userPermissions: {
+ adminSnippet: true,
+ updateSnippet: true,
+ reportSnippet: false,
+ },
+ project: null,
+ author: {
+ name: 'Thor Odinson',
+ },
+ blob: {
+ binary: false,
},
};
const mutationVariables = {
mutation: DeleteSnippetMutation,
variables: {
- id: snippet.snippet.id,
+ id: snippet.id,
},
};
const errorMsg = 'Foo bar';
@@ -46,10 +47,12 @@ describe('Snippet header component', () => {
loading = false,
permissions = {},
mutationRes = mutationTypes.RESOLVE,
+ snippetProps = {},
} = {}) {
- const defaultProps = Object.assign({}, snippet);
+ // const defaultProps = Object.assign({}, snippet, snippetProps);
+ const defaultProps = Object.assign(snippet, snippetProps);
if (permissions) {
- Object.assign(defaultProps.snippet.userPermissions, {
+ Object.assign(defaultProps.userPermissions, {
...permissions,
});
}
@@ -65,7 +68,9 @@ describe('Snippet header component', () => {
wrapper = shallowMount(SnippetHeader, {
mocks: { $apollo },
propsData: {
- ...defaultProps,
+ snippet: {
+ ...defaultProps,
+ },
},
stubs: {
ApolloMutation,
@@ -126,6 +131,17 @@ describe('Snippet header component', () => {
expect(wrapper.find(GlModal).exists()).toBe(true);
});
+ it('renders Edit button as disabled for binary snippets', () => {
+ createComponent({
+ snippetProps: {
+ blob: {
+ binary: true,
+ },
+ },
+ });
+ expect(wrapper.find('[href*="edit"]').props('disabled')).toBe(true);
+ });
+
describe('Delete mutation', () => {
const { location } = window;
diff --git a/spec/graphql/mutations/alert_management/update_alert_status_spec.rb b/spec/graphql/mutations/alert_management/update_alert_status_spec.rb
new file mode 100644
index 00000000000..3cd5e217571
--- /dev/null
+++ b/spec/graphql/mutations/alert_management/update_alert_status_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Mutations::AlertManagement::UpdateAlertStatus do
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:alert) { create(:alert_management_alert, status: 'triggered') }
+ let_it_be(:project) { alert.project }
+ let(:new_status) { 'acknowledged' }
+ let(:args) { { status: new_status, project_path: project.full_path, iid: alert.iid } }
+
+ specify { expect(described_class).to require_graphql_authorizations(:update_alert_management_alerts) }
+
+ describe '#resolve' do
+ subject(:resolve) { mutation_for(project, current_user).resolve(args) }
+
+ context 'user has access to project' do
+ before do
+ project.add_developer(current_user)
+ end
+
+ it 'changes the status' do
+ expect { resolve }.to change { alert.reload.status }.from(alert.status).to(new_status)
+ end
+
+ it 'returns the alert with no errors' do
+ expect(resolve).to eq(
+ alert: alert,
+ errors: []
+ )
+ end
+
+ context 'error occurs when updating' do
+ it 'returns the alert with errors' do
+ # Stub an error on the alert
+ allow_next_instance_of(Resolvers::AlertManagementAlertResolver) do |resolver|
+ allow(resolver).to receive(:resolve).and_return(alert)
+ end
+
+ allow(alert).to receive(:save).and_return(false)
+ allow(alert).to receive(:errors).and_return(
+ double(full_messages: %w(foo bar))
+ )
+ expect(resolve).to eq(
+ alert: alert,
+ errors: ['foo and bar']
+ )
+ end
+
+ context 'invalid status given' do
+ let(:new_status) { 'invalid_status' }
+
+ it 'returns the alert with errors' do
+ expect(resolve).to eq(
+ alert: alert,
+ errors: ['Invalid status']
+ )
+ end
+ end
+ end
+ end
+
+ it 'raises an error if the resource is not accessible to the user' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ private
+
+ def mutation_for(project, user)
+ described_class.new(object: project, context: { current_user: user }, field: nil)
+ end
+end
diff --git a/spec/graphql/types/alert_management/alert_type_spec.rb b/spec/graphql/types/alert_management/alert_type_spec.rb
index f66a135171e..169611b2e18 100644
--- a/spec/graphql/types/alert_management/alert_type_spec.rb
+++ b/spec/graphql/types/alert_management/alert_type_spec.rb
@@ -11,13 +11,18 @@ describe GitlabSchema.types['AlertManagementAlert'] do
expected_fields = %i[
iid
title
+ description
severity
status
service
monitoring_tool
+ hosts
started_at
ended_at
event_count
+ details
+ created_at
+ updated_at
]
expect(described_class).to have_graphql_fields(*expected_fields)
diff --git a/spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb b/spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb
index 98659dbed57..c1dab5feb91 100644
--- a/spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb
+++ b/spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb
@@ -84,6 +84,16 @@ describe Gitlab::Graphql::Authorize::AuthorizeFieldService do
end
end
+ context 'when the field is a connection' do
+ context 'when it resolves to nil' do
+ let(:field) { type_with_field(Types::QueryType.connection_type, :read_field, nil).fields['testField'].to_graphql }
+
+ it 'does not fail when authorizing' do
+ expect(resolved).to be_nil
+ end
+ end
+ end
+
context 'when the field is a specific type' do
let(:custom_type) { type(:read_type) }
let(:object_in_field) { double('presented in field') }
diff --git a/spec/lib/gitlab/kubernetes/helm/api_spec.rb b/spec/lib/gitlab/kubernetes/helm/api_spec.rb
index 8147990ecc3..1f925fd45af 100644
--- a/spec/lib/gitlab/kubernetes/helm/api_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/api_spec.rb
@@ -92,7 +92,6 @@ describe Gitlab::Kubernetes::Helm::API do
allow(client).to receive(:get_config_map).and_return(nil)
allow(client).to receive(:create_config_map).and_return(nil)
allow(client).to receive(:create_service_account).and_return(nil)
- allow(client).to receive(:create_cluster_role_binding).and_return(nil)
allow(client).to receive(:delete_pod).and_return(nil)
allow(namespace).to receive(:ensure_exists!).once
end
@@ -136,7 +135,7 @@ describe Gitlab::Kubernetes::Helm::API do
context 'without a service account' do
it 'does not create a service account on kubeclient' do
expect(client).not_to receive(:create_service_account)
- expect(client).not_to receive(:create_cluster_role_binding)
+ expect(client).not_to receive(:update_cluster_role_binding)
subject.install(command)
end
@@ -160,15 +159,14 @@ describe Gitlab::Kubernetes::Helm::API do
)
end
- context 'service account and cluster role binding does not exist' do
+ context 'service account does not exist' do
before do
expect(client).to receive(:get_service_account).with('tiller', 'gitlab-managed-apps').and_raise(Kubeclient::ResourceNotFoundError.new(404, 'Not found', nil))
- expect(client).to receive(:get_cluster_role_binding).with('tiller-admin').and_raise(Kubeclient::ResourceNotFoundError.new(404, 'Not found', nil))
end
it 'creates a service account, followed the cluster role binding on kubeclient' do
expect(client).to receive(:create_service_account).with(service_account_resource).once.ordered
- expect(client).to receive(:create_cluster_role_binding).with(cluster_role_binding_resource).once.ordered
+ expect(client).to receive(:update_cluster_role_binding).with(cluster_role_binding_resource).once.ordered
subject.install(command)
end
@@ -177,21 +175,6 @@ describe Gitlab::Kubernetes::Helm::API do
context 'service account already exists' do
before do
expect(client).to receive(:get_service_account).with('tiller', 'gitlab-managed-apps').and_return(service_account_resource)
- expect(client).to receive(:get_cluster_role_binding).with('tiller-admin').and_raise(Kubeclient::ResourceNotFoundError.new(404, 'Not found', nil))
- end
-
- it 'updates the service account, followed by creating the cluster role binding' do
- expect(client).to receive(:update_service_account).with(service_account_resource).once.ordered
- expect(client).to receive(:create_cluster_role_binding).with(cluster_role_binding_resource).once.ordered
-
- subject.install(command)
- end
- end
-
- context 'service account and cluster role binding already exists' do
- before do
- expect(client).to receive(:get_service_account).with('tiller', 'gitlab-managed-apps').and_return(service_account_resource)
- expect(client).to receive(:get_cluster_role_binding).with('tiller-admin').and_return(cluster_role_binding_resource)
end
it 'updates the service account, followed by creating the cluster role binding' do
@@ -216,7 +199,7 @@ describe Gitlab::Kubernetes::Helm::API do
context 'legacy abac cluster' do
it 'does not create a service account on kubeclient' do
expect(client).not_to receive(:create_service_account)
- expect(client).not_to receive(:create_cluster_role_binding)
+ expect(client).not_to receive(:update_cluster_role_binding)
subject.install(command)
end
diff --git a/spec/lib/gitlab/kubernetes/kube_client_spec.rb b/spec/lib/gitlab/kubernetes/kube_client_spec.rb
index 56838f2cb7a..af328eaec9a 100644
--- a/spec/lib/gitlab/kubernetes/kube_client_spec.rb
+++ b/spec/lib/gitlab/kubernetes/kube_client_spec.rb
@@ -234,8 +234,6 @@ describe Gitlab::Kubernetes::KubeClient do
:create_role,
:get_role,
:update_role,
- :create_cluster_role_binding,
- :get_cluster_role_binding,
:update_cluster_role_binding
].each do |method|
describe "##{method}" do
@@ -354,6 +352,16 @@ describe Gitlab::Kubernetes::KubeClient do
end
end
+ shared_examples 'create_or_update method using put' do
+ let(:update_method) { "update_#{resource_type}" }
+
+ it 'calls the update method' do
+ expect(client).to receive(update_method).with(resource)
+
+ subject
+ end
+ end
+
shared_examples 'create_or_update method' do
let(:get_method) { "get_#{resource_type}" }
let(:update_method) { "update_#{resource_type}" }
@@ -393,7 +401,7 @@ describe Gitlab::Kubernetes::KubeClient do
subject { client.create_or_update_cluster_role_binding(resource) }
- it_behaves_like 'create_or_update method'
+ it_behaves_like 'create_or_update method using put'
end
describe '#create_or_update_role_binding' do
@@ -405,7 +413,7 @@ describe Gitlab::Kubernetes::KubeClient do
subject { client.create_or_update_role_binding(resource) }
- it_behaves_like 'create_or_update method'
+ it_behaves_like 'create_or_update method using put'
end
describe '#create_or_update_service_account' do
diff --git a/spec/lib/gitlab/sidekiq_config/cli_methods_spec.rb b/spec/lib/gitlab/sidekiq_config/cli_methods_spec.rb
index 0aaff12f278..80e8da58f23 100644
--- a/spec/lib/gitlab/sidekiq_config/cli_methods_spec.rb
+++ b/spec/lib/gitlab/sidekiq_config/cli_methods_spec.rb
@@ -54,14 +54,6 @@ describe Gitlab::SidekiqConfig::CliMethods do
end
end
- context 'when the file contains an array of strings' do
- before do
- stub_contents(['queue_a'], ['queue_b'])
- end
-
- include_examples 'valid file contents'
- end
-
context 'when the file contains an array of hashes' do
before do
stub_contents([{ name: 'queue_a' }], [{ name: 'queue_b' }])
diff --git a/spec/models/alert_management/alert_spec.rb b/spec/models/alert_management/alert_spec.rb
index 2e552875252..b40b18aeb4c 100644
--- a/spec/models/alert_management/alert_spec.rb
+++ b/spec/models/alert_management/alert_spec.rb
@@ -126,4 +126,30 @@ describe AlertManagement::Alert do
it { is_expected.to match_array(alert_1) }
end
+
+ describe '.details' do
+ let(:payload) do
+ {
+ 'title' => 'Details title',
+ 'custom' => {
+ 'alert' => {
+ 'fields' => %w[one two]
+ }
+ },
+ 'yet' => {
+ 'another' => 'field'
+ }
+ }
+ end
+ let(:alert) { build(:alert_management_alert, title: 'Details title', payload: payload) }
+
+ subject { alert.details }
+
+ it 'renders the payload as inline hash' do
+ is_expected.to eq(
+ 'custom.alert.fields' => %w[one two],
+ 'yet.another' => 'field'
+ )
+ end
+ end
end
diff --git a/spec/models/ci/persistent_ref_spec.rb b/spec/models/ci/persistent_ref_spec.rb
index 4cece0664cf..89dd9b05331 100644
--- a/spec/models/ci/persistent_ref_spec.rb
+++ b/spec/models/ci/persistent_ref_spec.rb
@@ -45,18 +45,6 @@ describe Ci::PersistentRef do
expect(pipeline.persistent_ref).to be_exist
end
- context 'when depend_on_persistent_pipeline_ref feature flag is disabled' do
- before do
- stub_feature_flags(depend_on_persistent_pipeline_ref: false)
- end
-
- it 'does not create a persistent ref' do
- expect(project.repository).not_to receive(:create_ref)
-
- subject
- end
- end
-
context 'when sha does not exist in the repository' do
let(:sha) { 'not-exist' }
diff --git a/spec/policies/alert_management/alert_policy_spec.rb b/spec/policies/alert_management/alert_policy_spec.rb
index 698264e4ac7..523464d8ff1 100644
--- a/spec/policies/alert_management/alert_policy_spec.rb
+++ b/spec/policies/alert_management/alert_policy_spec.rb
@@ -6,17 +6,20 @@ describe AlertManagement::AlertPolicy, :models do
let(:alert) { create(:alert_management_alert) }
let(:project) { alert.project }
let(:user) { create(:user) }
- let(:policy) { described_class.new(user, alert) }
+
+ subject(:policy) { described_class.new(user, alert) }
describe 'rules' do
- it { expect(policy).to be_disallowed :read_alert_management_alerts }
+ it { is_expected.to be_disallowed :read_alert_management_alerts }
+ it { is_expected.to be_disallowed :update_alert_management_alerts }
context 'when developer' do
before do
project.add_developer(user)
end
- it { expect(policy).to be_allowed :read_alert_management_alerts }
+ it { is_expected.to be_allowed :read_alert_management_alerts }
+ it { is_expected.to be_allowed :update_alert_management_alerts }
end
end
end
diff --git a/spec/presenters/ci/build_runner_presenter_spec.rb b/spec/presenters/ci/build_runner_presenter_spec.rb
index 28806a1c7e3..6b2b031dbd0 100644
--- a/spec/presenters/ci/build_runner_presenter_spec.rb
+++ b/spec/presenters/ci/build_runner_presenter_spec.rb
@@ -173,81 +173,34 @@ describe Ci::BuildRunnerPresenter do
let(:pipeline) { merge_request.all_pipelines.first }
let(:build) { create(:ci_build, ref: pipeline.ref, pipeline: pipeline) }
- context 'when depend_on_persistent_pipeline_ref feature flag is enabled' do
- before do
- stub_feature_flags(ci_force_exposing_merge_request_refs: false)
- pipeline.persistent_ref.create
- end
-
- it 'returns the correct refspecs' do
- is_expected
- .to contain_exactly("+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}")
- end
-
- context 'when ci_force_exposing_merge_request_refs feature flag is enabled' do
- before do
- stub_feature_flags(ci_force_exposing_merge_request_refs: true)
- end
-
- it 'returns the correct refspecs' do
- is_expected
- .to contain_exactly("+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}",
- '+refs/merge-requests/1/head:refs/merge-requests/1/head')
- end
- end
-
- context 'when GIT_DEPTH is zero' do
- before do
- create(:ci_pipeline_variable, key: 'GIT_DEPTH', value: 0, pipeline: build.pipeline)
- end
-
- it 'returns the correct refspecs' do
- is_expected
- .to contain_exactly("+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}",
- '+refs/heads/*:refs/remotes/origin/*',
- '+refs/tags/*:refs/tags/*')
- end
- end
-
- context 'when pipeline is legacy detached merge request pipeline' do
- let(:merge_request) { create(:merge_request, :with_legacy_detached_merge_request_pipeline) }
+ before do
+ pipeline.persistent_ref.create
+ end
- it 'returns the correct refspecs' do
- is_expected.to contain_exactly("+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}",
- "+refs/heads/#{build.ref}:refs/remotes/origin/#{build.ref}")
- end
- end
+ it 'returns the correct refspecs' do
+ is_expected
+ .to contain_exactly("+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}")
end
- context 'when depend_on_persistent_pipeline_ref feature flag is disabled' do
+ context 'when GIT_DEPTH is zero' do
before do
- stub_feature_flags(depend_on_persistent_pipeline_ref: false)
+ create(:ci_pipeline_variable, key: 'GIT_DEPTH', value: 0, pipeline: build.pipeline)
end
it 'returns the correct refspecs' do
is_expected
- .to contain_exactly('+refs/merge-requests/1/head:refs/merge-requests/1/head')
- end
-
- context 'when GIT_DEPTH is zero' do
- before do
- create(:ci_pipeline_variable, key: 'GIT_DEPTH', value: 0, pipeline: build.pipeline)
- end
-
- it 'returns the correct refspecs' do
- is_expected
- .to contain_exactly('+refs/merge-requests/1/head:refs/merge-requests/1/head',
- '+refs/heads/*:refs/remotes/origin/*',
- '+refs/tags/*:refs/tags/*')
- end
+ .to contain_exactly("+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}",
+ '+refs/heads/*:refs/remotes/origin/*',
+ '+refs/tags/*:refs/tags/*')
end
+ end
- context 'when pipeline is legacy detached merge request pipeline' do
- let(:merge_request) { create(:merge_request, :with_legacy_detached_merge_request_pipeline) }
+ context 'when pipeline is legacy detached merge request pipeline' do
+ let(:merge_request) { create(:merge_request, :with_legacy_detached_merge_request_pipeline) }
- it 'returns the correct refspecs' do
- is_expected.to contain_exactly("+refs/heads/#{build.ref}:refs/remotes/origin/#{build.ref}")
- end
+ it 'returns the correct refspecs' do
+ is_expected.to contain_exactly("+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}",
+ "+refs/heads/#{build.ref}:refs/remotes/origin/#{build.ref}")
end
end
end
diff --git a/spec/requests/api/graphql/project/alert_management/alerts_spec.rb b/spec/requests/api/graphql/project/alert_management/alerts_spec.rb
index 6722357f76d..5d60f054bf2 100644
--- a/spec/requests/api/graphql/project/alert_management/alerts_spec.rb
+++ b/spec/requests/api/graphql/project/alert_management/alerts_spec.rb
@@ -4,10 +4,11 @@ require 'spec_helper'
describe 'getting Alert Management Alerts' do
include GraphqlHelpers
+ let_it_be(:payload) { { 'custom' => { 'alert' => 'payload' } } }
let_it_be(:project) { create(:project, :repository) }
let_it_be(:current_user) { create(:user) }
let_it_be(:alert_1) { create(:alert_management_alert, :all_fields, project: project) }
- let_it_be(:alert_2) { create(:alert_management_alert, :all_fields, project: project) }
+ let_it_be(:alert_2) { create(:alert_management_alert, :all_fields, project: project, payload: payload) }
let(:fields) do
<<~QUERY
@@ -55,13 +56,18 @@ describe 'getting Alert Management Alerts' do
expect(first_alert).to include(
'iid' => alert_2.iid.to_s,
'title' => alert_2.title,
+ 'description' => alert_2.description,
'severity' => alert_2.severity.upcase,
'status' => alert_2.status.upcase,
'monitoringTool' => alert_2.monitoring_tool,
'service' => alert_2.service,
+ 'hosts' => alert_2.hosts,
'eventCount' => alert_2.events,
'startedAt' => alert_2.started_at.strftime('%Y-%m-%dT%H:%M:%SZ'),
- 'endedAt' => alert_2.ended_at.strftime('%Y-%m-%dT%H:%M:%SZ')
+ 'endedAt' => alert_2.ended_at.strftime('%Y-%m-%dT%H:%M:%SZ'),
+ 'details' => { 'custom.alert' => 'payload' },
+ 'createdAt' => alert_2.created_at.strftime('%Y-%m-%dT%H:%M:%SZ'),
+ 'updatedAt' => alert_2.updated_at.strftime('%Y-%m-%dT%H:%M:%SZ')
)
end
diff --git a/spec/services/alert_management/update_alert_status_service_spec.rb b/spec/services/alert_management/update_alert_status_service_spec.rb
new file mode 100644
index 00000000000..325b03840d3
--- /dev/null
+++ b/spec/services/alert_management/update_alert_status_service_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe AlertManagement::UpdateAlertStatusService do
+ let_it_be(:alert) { create(:alert_management_alert, status: 'triggered') }
+
+ describe '#execute' do
+ subject(:execute) { described_class.new(alert, new_status).execute }
+
+ let(:new_status) { 'acknowledged' }
+
+ it 'updates the status' do
+ expect { execute }.to change { alert.status }.to(new_status)
+ end
+
+ context 'with unknown status' do
+ let(:new_status) { 'random_status' }
+
+ it 'returns an error' do
+ expect(execute.status).to eq(:error)
+ end
+
+ it 'does not update the status' do
+ expect { execute }.not_to change { alert.status }
+ end
+ end
+ end
+end
diff --git a/spec/services/clusters/gcp/finalize_creation_service_spec.rb b/spec/services/clusters/gcp/finalize_creation_service_spec.rb
index 43dbea959a2..4d1548c9786 100644
--- a/spec/services/clusters/gcp/finalize_creation_service_spec.rb
+++ b/spec/services/clusters/gcp/finalize_creation_service_spec.rb
@@ -108,8 +108,7 @@ describe Clusters::Gcp::FinalizeCreationService, '#execute' do
}
)
- stub_kubeclient_get_cluster_role_binding_error(api_url, 'gitlab-admin')
- stub_kubeclient_create_cluster_role_binding(api_url)
+ stub_kubeclient_put_cluster_role_binding(api_url, 'gitlab-admin')
end
end
diff --git a/spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb b/spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb
index 3982d2310d8..6d8b1617c17 100644
--- a/spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb
+++ b/spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb
@@ -28,7 +28,6 @@ describe Clusters::Kubernetes::CreateOrUpdateNamespaceService, '#execute' do
stub_kubeclient_get_secret_error(api_url, 'gitlab-token')
stub_kubeclient_create_secret(api_url)
- stub_kubeclient_get_role_binding(api_url, "gitlab-#{namespace}", namespace: namespace)
stub_kubeclient_put_role_binding(api_url, "gitlab-#{namespace}", namespace: namespace)
stub_kubeclient_get_namespace(api_url, namespace: namespace)
stub_kubeclient_get_service_account_error(api_url, "#{namespace}-service-account", namespace: namespace)
diff --git a/spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb b/spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb
index 8fa22422074..4bcd5c6933e 100644
--- a/spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb
+++ b/spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb
@@ -83,8 +83,7 @@ describe Clusters::Kubernetes::CreateOrUpdateServiceAccountService do
before do
cluster.platform_kubernetes.rbac!
- stub_kubeclient_get_cluster_role_binding_error(api_url, cluster_role_binding_name)
- stub_kubeclient_create_cluster_role_binding(api_url)
+ stub_kubeclient_put_cluster_role_binding(api_url, cluster_role_binding_name)
end
it_behaves_like 'creates service account and token'
@@ -92,9 +91,8 @@ describe Clusters::Kubernetes::CreateOrUpdateServiceAccountService do
it 'creates a cluster role binding with cluster-admin access' do
subject
- expect(WebMock).to have_requested(:post, api_url + "/apis/rbac.authorization.k8s.io/v1/clusterrolebindings").with(
+ expect(WebMock).to have_requested(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/clusterrolebindings/gitlab-admin").with(
body: hash_including(
- kind: 'ClusterRoleBinding',
metadata: { name: 'gitlab-admin' },
roleRef: {
apiGroup: 'rbac.authorization.k8s.io',
@@ -143,8 +141,7 @@ describe Clusters::Kubernetes::CreateOrUpdateServiceAccountService do
before do
cluster.platform_kubernetes.rbac!
- stub_kubeclient_get_role_binding_error(api_url, role_binding_name, namespace: namespace)
- stub_kubeclient_create_role_binding(api_url, namespace: namespace)
+ stub_kubeclient_put_role_binding(api_url, role_binding_name, namespace: namespace)
stub_kubeclient_put_role(api_url, Clusters::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_NAME, namespace: namespace)
stub_kubeclient_put_role_binding(api_url, Clusters::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_BINDING_NAME, namespace: namespace)
stub_kubeclient_put_role(api_url, Clusters::Kubernetes::GITLAB_CROSSPLANE_DATABASE_ROLE_NAME, namespace: namespace)
@@ -166,9 +163,8 @@ describe Clusters::Kubernetes::CreateOrUpdateServiceAccountService do
it 'creates a namespaced role binding with edit access' do
subject
- expect(WebMock).to have_requested(:post, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings").with(
+ expect(WebMock).to have_requested(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings/#{role_binding_name}").with(
body: hash_including(
- kind: 'RoleBinding',
metadata: { name: "gitlab-#{namespace}", namespace: "#{namespace}" },
roleRef: {
apiGroup: 'rbac.authorization.k8s.io',
diff --git a/spec/services/spam/spam_action_service_spec.rb b/spec/services/spam/spam_action_service_spec.rb
index ff60eacc79d..560833aba97 100644
--- a/spec/services/spam/spam_action_service_spec.rb
+++ b/spec/services/spam/spam_action_service_spec.rb
@@ -73,11 +73,13 @@ describe Spam::SpamActionService do
describe '#execute' do
let(:request) { double(:request, env: env) }
let(:fake_verdict_service) { double(:spam_verdict_service) }
+ let(:allowlisted) { false }
let_it_be(:existing_spam_log) { create(:spam_log, user: user, recaptcha_verified: false) }
subject do
described_service = described_class.new(spammable: issue, request: request)
+ allow(described_service).to receive(:allowlisted?).and_return(allowlisted)
described_service.execute(user: user, api: nil, recaptcha_verified: recaptcha_verified, spam_log_id: existing_spam_log.id)
end
@@ -121,6 +123,16 @@ describe Spam::SpamActionService do
issue.description = 'SPAM!'
end
+ context 'if allowlisted' do
+ let(:allowlisted) { true }
+
+ it 'does not perform spam check' do
+ expect(Spam::SpamVerdictService).not_to receive(:new)
+
+ subject
+ end
+ end
+
context 'when disallowed by the spam verdict service' do
before do
allow(fake_verdict_service).to receive(:execute).and_return(DISALLOW)
diff --git a/spec/support/helpers/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb
index ca910e47695..6d33d1f213f 100644
--- a/spec/support/helpers/kubernetes_helpers.rb
+++ b/spec/support/helpers/kubernetes_helpers.rb
@@ -201,28 +201,8 @@ module KubernetesHelpers
.to_return(kube_response({}))
end
- def stub_kubeclient_get_cluster_role_binding_error(api_url, name, status: 404)
- WebMock.stub_request(:get, api_url + "/apis/rbac.authorization.k8s.io/v1/clusterrolebindings/#{name}")
- .to_return(status: [status, "Internal Server Error"])
- end
-
- def stub_kubeclient_create_cluster_role_binding(api_url)
- WebMock.stub_request(:post, api_url + '/apis/rbac.authorization.k8s.io/v1/clusterrolebindings')
- .to_return(kube_response({}))
- end
-
- def stub_kubeclient_get_role_binding(api_url, name, namespace: 'default')
- WebMock.stub_request(:get, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings/#{name}")
- .to_return(kube_response({}))
- end
-
- def stub_kubeclient_get_role_binding_error(api_url, name, namespace: 'default', status: 404)
- WebMock.stub_request(:get, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings/#{name}")
- .to_return(status: [status, "Internal Server Error"])
- end
-
- def stub_kubeclient_create_role_binding(api_url, namespace: 'default')
- WebMock.stub_request(:post, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings")
+ def stub_kubeclient_put_cluster_role_binding(api_url, name)
+ WebMock.stub_request(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/clusterrolebindings/#{name}")
.to_return(kube_response({}))
end
diff --git a/yarn.lock b/yarn.lock
index 0a4ef63d580..1fa766a6274 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -787,10 +787,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.125.0.tgz#59c667dae8f7e4c80b482f5f6cc35367c016387b"
integrity sha512-MKfFYa8f+9P2tJ/JN/E9oDBSSo/gRz2zuGui4XHQPoaw/DkIMn7EyAzeSpRgbgs1LgMcEqqKsIEx+spCga3jsQ==
-"@gitlab/ui@13.9.0":
- version "13.9.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-13.9.0.tgz#c75c3c6adc92e71a5e7915fe6d1c9fa6e2d5f85d"
- integrity sha512-fpjjMXAyOGIITR/Jb7zmw7ul5EAwdSdivmJsiQnwb9eetjNgVlguYu0ZZM0YAdgRXeeIRyVaS8OCqTeyD02yFQ==
+"@gitlab/ui@14.0.0":
+ version "14.0.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-14.0.0.tgz#d478b1454659c0f54b72cdecce1c2014fc5f8564"
+ integrity sha512-R+unP0mOBYQ+uRJLm/tI+2znsbsHY2rumSYtMqM3vGCXasteySQIMZ8huWGa5Cf4ZUdy1lNa0J/zxKj6TLdjCQ==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"