Commit a77db6bc authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 6cf6996f
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import { mapState } from 'vuex';
import { GlTooltipDirective } from '@gitlab/ui'; import { GlTooltipDirective } from '@gitlab/ui';
import { sprintf, __ } from '~/locale'; import { sprintf, __ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
...@@ -63,6 +64,7 @@ export default { ...@@ -63,6 +64,7 @@ export default {
}; };
}, },
computed: { computed: {
...mapState(['isShowingLabels']),
numberOverLimit() { numberOverLimit() {
return this.issue.assignees.length - this.limitBeforeCounter; return this.issue.assignees.length - this.limitBeforeCounter;
}, },
...@@ -92,7 +94,7 @@ export default { ...@@ -92,7 +94,7 @@ export default {
return false; return false;
}, },
showLabelFooter() { showLabelFooter() {
return this.issue.labels.find(l => this.showLabel(l)) !== undefined; return this.isShowingLabels && this.issue.labels.find(this.showLabel);
}, },
issueReferencePath() { issueReferencePath() {
const { referencePath, groupId } = this.issue; const { referencePath, groupId } = this.issue;
......
...@@ -13,6 +13,7 @@ import 'ee_else_ce/boards/models/issue'; ...@@ -13,6 +13,7 @@ import 'ee_else_ce/boards/models/issue';
import 'ee_else_ce/boards/models/list'; import 'ee_else_ce/boards/models/list';
import '~/boards/models/milestone'; import '~/boards/models/milestone';
import '~/boards/models/project'; import '~/boards/models/project';
import store from '~/boards/stores';
import boardsStore from '~/boards/stores/boards_store'; import boardsStore from '~/boards/stores/boards_store';
import ModalStore from '~/boards/stores/modal_store'; import ModalStore from '~/boards/stores/modal_store';
import BoardService from 'ee_else_ce/boards/services/board_service'; import BoardService from 'ee_else_ce/boards/services/board_service';
...@@ -29,6 +30,7 @@ import { ...@@ -29,6 +30,7 @@ import {
} from '~/lib/utils/common_utils'; } from '~/lib/utils/common_utils';
import boardConfigToggle from 'ee_else_ce/boards/config_toggle'; import boardConfigToggle from 'ee_else_ce/boards/config_toggle';
import toggleFocusMode from 'ee_else_ce/boards/toggle_focus'; import toggleFocusMode from 'ee_else_ce/boards/toggle_focus';
import toggleLabels from 'ee_else_ce/boards/toggle_labels';
import { import {
setPromotionState, setPromotionState,
setWeigthFetchingState, setWeigthFetchingState,
...@@ -67,6 +69,7 @@ export default () => { ...@@ -67,6 +69,7 @@ export default () => {
BoardSidebar, BoardSidebar,
BoardAddIssuesModal, BoardAddIssuesModal,
}, },
store,
data: { data: {
state: boardsStore.state, state: boardsStore.state,
loading: true, loading: true,
...@@ -314,5 +317,6 @@ export default () => { ...@@ -314,5 +317,6 @@ export default () => {
} }
toggleFocusMode(ModalStore, boardsStore, $boardApp); toggleFocusMode(ModalStore, boardsStore, $boardApp);
toggleLabels();
mountMultipleBoardsSwitcher(); mountMultipleBoardsSwitcher();
}; };
export default {
getLabelToggleState: state => (state.isShowingLabels ? 'on' : 'off'),
};
import Vue from 'vue'; import Vue from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import state from 'ee_else_ce/boards/stores/state'; import state from 'ee_else_ce/boards/stores/state';
import getters from 'ee_else_ce/boards/stores/getters';
import actions from 'ee_else_ce/boards/stores/actions'; import actions from 'ee_else_ce/boards/stores/actions';
import mutations from 'ee_else_ce/boards/stores/mutations'; import mutations from 'ee_else_ce/boards/stores/mutations';
Vue.use(Vuex); Vue.use(Vuex);
export default () => export const createStore = () =>
new Vuex.Store({ new Vuex.Store({
state, state,
getters,
actions, actions,
mutations, mutations,
}); });
export default createStore();
export default () => ({ export default () => ({
// ... isShowingLabels: true,
}); });
...@@ -545,3 +545,15 @@ ...@@ -545,3 +545,15 @@
.board-issue-path.js-show-tooltip { .board-issue-path.js-show-tooltip {
cursor: help; cursor: help;
} }
.board-labels-toggle-wrapper {
/**
* Make the wrapper the same height as a button so it aligns properly when the
* filtered-search-box input element increases in size on Linux smaller breakpoints
*/
height: 34px;
@include media-breakpoint-down(sm) {
margin-bottom: 10px;
}
}
...@@ -9,25 +9,11 @@ module Projects ...@@ -9,25 +9,11 @@ module Projects
tag_names = params[:tags] tag_names = params[:tags]
return error('not tags specified') if tag_names.blank? return error('not tags specified') if tag_names.blank?
if can_use? smart_delete(container_repository, tag_names)
smart_delete(container_repository, tag_names)
else
unsafe_delete(container_repository, tag_names)
end
end end
private private
def unsafe_delete(container_repository, tag_names)
deleted_tags = tag_names.select do |tag_name|
container_repository.tag(tag_name).unsafe_delete
end
return error('could not delete tags') if deleted_tags.empty?
success(deleted: deleted_tags)
end
# Replace a tag on the registry with a dummy tag. # Replace a tag on the registry with a dummy tag.
# This is a hack as the registry doesn't support deleting individual # This is a hack as the registry doesn't support deleting individual
# tags. This code effectively pushes a dummy image and assigns the tag to it. # tags. This code effectively pushes a dummy image and assigns the tag to it.
...@@ -57,10 +43,6 @@ module Projects ...@@ -57,10 +43,6 @@ module Projects
error('could not delete tags') error('could not delete tags')
end end
end end
def can_use?
Feature.enabled?(:container_registry_smart_delete, project, default_enabled: true)
end
end end
end end
end end
...@@ -94,7 +94,8 @@ ...@@ -94,7 +94,8 @@
- else - else
= f.text_field :name, label: s_('Profiles|Full name'), required: true, title: s_("Profiles|Using emojis in names seems fun, but please try to set a status message instead"), wrapper: { class: 'col-md-9 qa-full-name rspec-full-name' }, help: s_("Profiles|Enter your name, so people you know can recognize you") = f.text_field :name, label: s_('Profiles|Full name'), required: true, title: s_("Profiles|Using emojis in names seems fun, but please try to set a status message instead"), wrapper: { class: 'col-md-9 qa-full-name rspec-full-name' }, help: s_("Profiles|Enter your name, so people you know can recognize you")
= f.text_field :id, readonly: true, label: s_('Profiles|User ID'), wrapper: { class: 'col-md-3' } = f.text_field :id, readonly: true, label: s_('Profiles|User ID'), wrapper: { class: 'col-md-3' }
= f.select :role, ::User.roles.keys.map { |role| [role.titleize, role] }, {}, class: 'input-md' - if experiment_enabled?(:signup_flow)
= f.select :role, ::User.roles.keys.map { |role| [role.titleize, role] }, { prompt: _('Select your role') }, class: 'input-md'
= render_if_exists 'profiles/email_settings', form: f = render_if_exists 'profiles/email_settings', form: f
= f.text_field :skype, class: 'input-md', placeholder: s_("Profiles|username") = f.text_field :skype, class: 'input-md', placeholder: s_("Profiles|username")
......
...@@ -147,6 +147,7 @@ ...@@ -147,6 +147,7 @@
%button.clear-search.hidden{ type: 'button' } %button.clear-search.hidden{ type: 'button' }
= icon('times') = icon('times')
#js-board-labels-toggle
.filter-dropdown-container.d-flex.flex-column.flex-md-row .filter-dropdown-container.d-flex.flex-column.flex-md-row
- if type == :boards - if type == :boards
.js-board-config{ data: { can_admin_list: user_can_admin_list, has_scope: board.scoped? } } .js-board-config{ data: { can_admin_list: user_can_admin_list, has_scope: board.scoped? } }
......
---
title: Adds a Sidekiq queue duration metric
merge_request: 19005
author:
type: other
...@@ -14,9 +14,13 @@ rackup 'config.ru' ...@@ -14,9 +14,13 @@ rackup 'config.ru'
pidfile '/home/git/gitlab/tmp/pids/puma.pid' pidfile '/home/git/gitlab/tmp/pids/puma.pid'
state_path '/home/git/gitlab/tmp/pids/puma.state' state_path '/home/git/gitlab/tmp/pids/puma.state'
stdout_redirect '/home/git/gitlab/log/puma.stdout.log', ## Uncomment the lines if you would like to write puma stdout & stderr streams
'/home/git/gitlab/log/puma.stderr.log', ## to a different location than rails logs.
true ## When using GitLab Development Kit, by default, these logs will be consumed
## by runit and can be accessed using `gdk tail rails-web`
# stdout_redirect '/home/git/gitlab/log/puma.stdout.log',
# '/home/git/gitlab/log/puma.stderr.log',
# true
# Configure "min" to be the minimum number of threads to use to answer # Configure "min" to be the minimum number of threads to use to answer
# requests and "max" the maximum. # requests and "max" the maximum.
......
...@@ -15,6 +15,8 @@ as much as possible. ...@@ -15,6 +15,8 @@ as much as possible.
The current stages are: The current stages are:
- `sync`: This stage is used to synchronize changes from gitlab-org/gitlab to
gitlab-org/gitlab-foss.
- `prepare`: This stage includes jobs that prepare artifacts that are needed by - `prepare`: This stage includes jobs that prepare artifacts that are needed by
jobs in subsequent stages. jobs in subsequent stages.
- `quick-test`: This stage includes test jobs that should run first and fail the - `quick-test`: This stage includes test jobs that should run first and fail the
......
...@@ -21,5 +21,49 @@ module Gitlab ...@@ -21,5 +21,49 @@ module Gitlab
payload[:rugged_duration_ms] = Gitlab::RuggedInstrumentation.query_time_ms payload[:rugged_duration_ms] = Gitlab::RuggedInstrumentation.query_time_ms
end end
end end
# Returns the queuing duration for a Sidekiq job in seconds, as a float, if the
# `enqueued_at` field or `created_at` field is available.
#
# * If the job doesn't contain sufficient information, returns nil
# * If the job has a start time in the future, returns 0
# * If the job contains an invalid start time value, returns nil
# @param [Hash] job a Sidekiq job, represented as a hash
def self.queue_duration_for_job(job)
# Old gitlab-shell messages don't provide enqueued_at/created_at attributes
enqueued_at = job['enqueued_at'] || job['created_at']
return unless enqueued_at
enqueued_at_time = convert_to_time(enqueued_at)
return unless enqueued_at_time
# Its possible that if theres clock-skew between two nodes
# this value may be less than zero. In that event, we record the value
# as zero.
[elapsed_by_absolute_time(enqueued_at_time), 0].max
end
# Calculates the time in seconds, as a float, from
# the provided start time until now
#
# @param [Time] start
def self.elapsed_by_absolute_time(start)
(Time.now - start).to_f.round(6)
end
private_class_method :elapsed_by_absolute_time
# Convert a representation of a time into a `Time` value
#
# @param time_value String, Float time representation, or nil
def self.convert_to_time(time_value)
return time_value if time_value.is_a?(Time)
return Time.iso8601(time_value) if time_value.is_a?(String)
return Time.at(time_value) if time_value.is_a?(Numeric) && time_value > 0
rescue ArgumentError
# Swallow invalid dates. Better to loose some observability
# than bring all background processing down because of a date
# formatting bug in a client
end
private_class_method :convert_to_time
end end
end end
...@@ -36,11 +36,8 @@ module Gitlab ...@@ -36,11 +36,8 @@ module Gitlab
payload['message'] = "#{base_message(payload)}: start" payload['message'] = "#{base_message(payload)}: start"
payload['job_status'] = 'start' payload['job_status'] = 'start'
# Old gitlab-shell messages don't provide enqueued_at/created_at attributes scheduling_latency_s = ::Gitlab::InstrumentationHelper.queue_duration_for_job(payload)
enqueued_at = payload['enqueued_at'] || payload['created_at'] payload['scheduling_latency_s'] = scheduling_latency_s if scheduling_latency_s
if enqueued_at
payload['scheduling_latency_s'] = elapsed_by_absolute_time(Time.iso8601(enqueued_at))
end
payload payload
end end
...@@ -98,10 +95,6 @@ module Gitlab ...@@ -98,10 +95,6 @@ module Gitlab
end end
end end
def elapsed_by_absolute_time(start)
(Time.now.utc - start).to_f.round(6)
end
def elapsed(t0) def elapsed(t0)
t1 = get_time t1 = get_time
{ {
......
...@@ -15,6 +15,9 @@ module Gitlab ...@@ -15,6 +15,9 @@ module Gitlab
def call(_worker, job, queue) def call(_worker, job, queue)
labels = create_labels(queue) labels = create_labels(queue)
queue_duration = ::Gitlab::InstrumentationHelper.queue_duration_for_job(job)
@metrics[:sidekiq_jobs_queue_duration_seconds].observe(labels, queue_duration) if queue_duration
@metrics[:sidekiq_running_jobs].increment(labels, 1) @metrics[:sidekiq_running_jobs].increment(labels, 1)
if job['retry_count'].present? if job['retry_count'].present?
...@@ -49,12 +52,13 @@ module Gitlab ...@@ -49,12 +52,13 @@ module Gitlab
def init_metrics def init_metrics
{ {
sidekiq_jobs_cpu_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_cpu_seconds, 'Seconds of cpu time to run sidekiq job', {}, SIDEKIQ_LATENCY_BUCKETS), sidekiq_jobs_cpu_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_cpu_seconds, 'Seconds of cpu time to run Sidekiq job', {}, SIDEKIQ_LATENCY_BUCKETS),
sidekiq_jobs_completion_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_completion_seconds, 'Seconds to complete sidekiq job', {}, SIDEKIQ_LATENCY_BUCKETS), sidekiq_jobs_completion_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_completion_seconds, 'Seconds to complete Sidekiq job', {}, SIDEKIQ_LATENCY_BUCKETS),
sidekiq_jobs_failed_total: ::Gitlab::Metrics.counter(:sidekiq_jobs_failed_total, 'Sidekiq jobs failed'), sidekiq_jobs_queue_duration_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_queue_duration_seconds, 'Duration in seconds that a Sidekiq job was queued before being executed', {}, SIDEKIQ_LATENCY_BUCKETS),
sidekiq_jobs_retried_total: ::Gitlab::Metrics.counter(:sidekiq_jobs_retried_total, 'Sidekiq jobs retried'), sidekiq_jobs_failed_total: ::Gitlab::Metrics.counter(:sidekiq_jobs_failed_total, 'Sidekiq jobs failed'),
sidekiq_running_jobs: ::Gitlab::Metrics.gauge(:sidekiq_running_jobs, 'Number of Sidekiq jobs running', {}, :all), sidekiq_jobs_retried_total: ::Gitlab::Metrics.counter(:sidekiq_jobs_retried_total, 'Sidekiq jobs retried'),
sidekiq_concurrency: ::Gitlab::Metrics.gauge(:sidekiq_concurrency, 'Maximum number of Sidekiq jobs', {}, :all) sidekiq_running_jobs: ::Gitlab::Metrics.gauge(:sidekiq_running_jobs, 'Number of Sidekiq jobs running', {}, :all),
sidekiq_concurrency: ::Gitlab::Metrics.gauge(:sidekiq_concurrency, 'Maximum number of Sidekiq jobs', {}, :all)
} }
end end
......
...@@ -8656,6 +8656,9 @@ msgstr[1] "" ...@@ -8656,6 +8656,9 @@ msgstr[1] ""
msgid "Hide values" msgid "Hide values"
msgstr "" msgstr ""
msgid "Hiding all labels"
msgstr ""
msgid "Highest number of requests per minute for each raw path, default to 300. To disable throttling set to 0." msgid "Highest number of requests per minute for each raw path, default to 300. To disable throttling set to 0."
msgstr "" msgstr ""
...@@ -14813,6 +14816,9 @@ msgstr "" ...@@ -14813,6 +14816,9 @@ msgstr ""
msgid "Select user" msgid "Select user"
msgstr "" msgstr ""
msgid "Select your role"
msgstr ""
msgid "Selected levels cannot be used by non-admin users for groups, projects or snippets. If the public level is restricted, user profiles are only visible to logged in users." msgid "Selected levels cannot be used by non-admin users for groups, projects or snippets. If the public level is restricted, user profiles are only visible to logged in users."
msgstr "" msgstr ""
...@@ -15178,6 +15184,9 @@ msgstr "" ...@@ -15178,6 +15184,9 @@ msgstr ""
msgid "Showing all issues" msgid "Showing all issues"
msgstr "" msgstr ""
msgid "Showing all labels"
msgstr ""
msgid "Showing last %{size} of log -" msgid "Showing last %{size} of log -"
msgstr "" msgstr ""
......
...@@ -66,6 +66,34 @@ describe 'User edit profile' do ...@@ -66,6 +66,34 @@ describe 'User edit profile' do
end end
end end
describe 'when I change my role' do
context 'experiment enabled' do
before do
stub_experiment_for_user(signup_flow: true)
visit(profile_path)
end
it 'changes my role' do
expect(page).to have_content 'Role'
select 'Data Analyst', from: 'user_role'
submit_settings
user.reload
expect(user.role).to eq 'data_analyst'
end
end
context 'experiment disabled' do
before do
stub_experiment_for_user(signup_flow: false)
visit(profile_path)
end
it 'does not show the role picker' do
expect(page).not_to have_content 'Role'
end
end
end
context 'user avatar' do context 'user avatar' do
before do before do
attach_file(:user_avatar, Rails.root.join('spec', 'fixtures', 'banana_sample.gif')) attach_file(:user_avatar, Rails.root.join('spec', 'fixtures', 'banana_sample.gif'))
......
import getters from '~/boards/stores/getters';
describe('Boards - Getters', () => {
describe('getLabelToggleState', () => {
it('should return "on" when isShowingLabels is true', () => {
const state = {
isShowingLabels: true,
};
expect(getters.getLabelToggleState(state)).toBe('on');
});
it('should return "off" when isShowingLabels is false', () => {
const state = {
isShowingLabels: false,
};
expect(getters.getLabelToggleState(state)).toBe('off');
});
});
});
...@@ -10,6 +10,7 @@ import eventHub from '~/boards/eventhub'; ...@@ -10,6 +10,7 @@ import eventHub from '~/boards/eventhub';
import '~/boards/models/label'; import '~/boards/models/label';
import '~/boards/models/assignee'; import '~/boards/models/assignee';
import '~/boards/models/list'; import '~/boards/models/list';
import store from '~/boards/stores';
import boardsStore from '~/boards/stores/boards_store'; import boardsStore from '~/boards/stores/boards_store';
import boardCard from '~/boards/components/board_card.vue'; import boardCard from '~/boards/components/board_card.vue';
import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data'; import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data';
...@@ -40,6 +41,7 @@ describe('Board card', () => { ...@@ -40,6 +41,7 @@ describe('Board card', () => {
list.issues[0].labels.push(label1); list.issues[0].labels.push(label1);
vm = new BoardCardComp({ vm = new BoardCardComp({
store,
propsData: { propsData: {
list, list,
issue: list.issues[0], issue: list.issues[0],
......
...@@ -10,6 +10,7 @@ import BoardList from '~/boards/components/board_list.vue'; ...@@ -10,6 +10,7 @@ import BoardList from '~/boards/components/board_list.vue';
import '~/boards/models/issue'; import '~/boards/models/issue';
import '~/boards/models/list'; import '~/boards/models/list';
import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data'; import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data';
import store from '~/boards/stores';
import boardsStore from '~/boards/stores/boards_store'; import boardsStore from '~/boards/stores/boards_store';
window.Sortable = Sortable; window.Sortable = Sortable;
...@@ -39,6 +40,7 @@ export default function createComponent({ done, listIssueProps = {}, componentPr ...@@ -39,6 +40,7 @@ export default function createComponent({ done, listIssueProps = {}, componentPr
const component = new BoardListComp({ const component = new BoardListComp({
el, el,
store,
propsData: { propsData: {
disabled: false, disabled: false,
list, list,
......
...@@ -10,6 +10,7 @@ import '~/boards/models/issue'; ...@@ -10,6 +10,7 @@ import '~/boards/models/issue';
import '~/boards/models/list'; import '~/boards/models/list';
import IssueCardInner from '~/boards/components/issue_card_inner.vue'; import IssueCardInner from '~/boards/components/issue_card_inner.vue';
import { listObj } from './mock_data'; import { listObj } from './mock_data';
import store from '~/boards/stores';
describe('Issue card component', () => { describe('Issue card component', () => {
const user = new ListAssignee({ const user = new ListAssignee({
...@@ -50,6 +51,7 @@ describe('Issue card component', () => { ...@@ -50,6 +51,7 @@ describe('Issue card component', () => {
component = new Vue({ component = new Vue({
el: document.querySelector('.test-container'), el: document.querySelector('.test-container'),
store,
components: { components: {
'issue-card': IssueCardInner, 'issue-card': IssueCardInner,
}, },
......
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rspec-parameterized'
describe Gitlab::InstrumentationHelper do
using RSpec::Parameterized::TableSyntax
describe '.queue_duration_for_job' do
where(:enqueued_at, :created_at, :time_now, :expected_duration) do
"2019-06-01T00:00:00.000+0000" | nil | "2019-06-01T02:00:00.000+0000" | 2.hours.to_f
"2019-06-01T02:00:00.000+0000" | nil | "2019-06-01T02:00:00.001+0000" | 0.001
"2019-06-01T02:00:00.000+0000" | "2019-05-01T02:00:00.000+0000" | "2019-06-01T02:00:01.000+0000" | 1
nil | "2019-06-01T02:00:00.000+0000" | "2019-06-01T02:00:00.001+0000" | 0.001
nil | nil | "2019-06-01T02:00:00.001+0000" | nil
"2019-06-01T02:00:00.000+0200" | nil | "2019-06-01T02:00:00.000-0200" | 4.hours.to_f
1571825569.998168 | nil | "2019-10-23T12:13:16.000+0200" | 26.001832
1571825569 | nil | "2019-10-23T12:13:16.000+0200" | 27
"invalid_date" | nil | "2019-10-23T12:13:16.000+0200" | nil
"" | nil | "2019-10-23T12:13:16.000+0200" | nil
0 | nil | "2019-10-23T12:13:16.000+0200" | nil
-1 | nil | "2019-10-23T12:13:16.000+0200" | nil
"2019-06-01T02:00:00.000+0000" | nil | "2019-06-01T00:00:00.000+0000" | 0
Time.at(1571999233) | nil | "2019-10-25T12:29:16.000+0200" | 123
end
with_them do
let(:job) { { 'enqueued_at' => enqueued_at, 'created_at' => created_at } }
it "returns the correct duration" do
Timecop.freeze(Time.iso8601(time_now)) do
expect(described_class.queue_duration_for_job(job)).to eq(expected_duration)
end
end
end
end
end
# frozen_string_literal: true # frozen_string_literal: true
require 'spec_helper' require 'fast_spec_helper'
describe Gitlab::SidekiqLogging::StructuredLogger do describe Gitlab::SidekiqLogging::StructuredLogger do
describe '#call' do describe '#call' do
......
# frozen_string_literal: true # frozen_string_literal: true
require 'spec_helper' require 'fast_spec_helper'
describe Gitlab::SidekiqMiddleware::Metrics do describe Gitlab::SidekiqMiddleware::Metrics do
let(:middleware) { described_class.new } let(:middleware) { described_class.new }
let(:concurrency_metric) { double('concurrency metric') } let(:concurrency_metric) { double('concurrency metric') }
let(:queue_duration_seconds) { double('queue duration seconds metric') }
let(:completion_seconds_metric) { double('completion seconds metric') } let(:completion_seconds_metric) { double('completion seconds metric') }
let(:user_execution_seconds_metric) { double('user execution seconds metric') } let(:user_execution_seconds_metric) { double('user execution seconds metric') }
let(:failed_total_metric) { double('failed total metric') } let(:failed_total_metric) { double('failed total metric') }
...@@ -13,6 +14,7 @@ describe Gitlab::SidekiqMiddleware::Metrics do ...@@ -13,6 +14,7 @@ describe Gitlab::SidekiqMiddleware::Metrics do
let(:running_jobs_metric) { double('running jobs metric') } let(:running_jobs_metric) { double('running jobs metric') }
before do before do
allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_jobs_queue_duration_seconds, anything, anything, anything).and_return(queue_duration_seconds)
allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_jobs_completion_seconds, anything, anything, anything).and_return(completion_seconds_metric) allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_jobs_completion_seconds, anything, anything, anything).and_return(completion_seconds_metric)
allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_jobs_cpu_seconds, anything, anything, anything).and_return(user_execution_seconds_metric) allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_jobs_cpu_seconds, anything, anything, anything).and_return(user_execution_seconds_metric)
allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_jobs_failed_total, anything).and_return(failed_total_metric) allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_jobs_failed_total, anything).and_return(failed_total_metric)
...@@ -20,7 +22,6 @@ describe Gitlab::SidekiqMiddleware::Metrics do ...@@ -20,7 +22,6 @@ describe Gitlab::SidekiqMiddleware::Metrics do
allow(Gitlab::Metrics).to receive(:gauge).with(:sidekiq_running_jobs, anything, {}, :all).and_return(running_jobs_metric) allow(Gitlab::Metrics).to receive(:gauge).with(:sidekiq_running_jobs, anything, {}, :all).and_return(running_jobs_metric)
allow(Gitlab::Metrics).to receive(:gauge).with(:sidekiq_concurrency, anything, {}, :all).and_return(concurrency_metric) allow(Gitlab::Metrics).to receive(:gauge).with(:sidekiq_concurrency, anything, {}, :all).and_return(concurrency_metric)
allow(running_jobs_metric).to receive(:increment)
allow(concurrency_metric).to receive(:set) allow(concurrency_metric).to receive(:set)
end end
...@@ -32,62 +33,76 @@ describe Gitlab::SidekiqMiddleware::Metrics do ...@@ -32,62 +33,76 @@ describe Gitlab::SidekiqMiddleware::Metrics do
end end
end end
it 'ignore user execution when measured 0' do
allow(completion_seconds_metric).to receive(:observe)
expect(user_execution_seconds_metric).not_to receive(:observe)
end
describe '#call' do describe '#call' do
let(:worker) { double(:worker) } let(:worker) { double(:worker) }
it 'yields block' do let(:job) { {} }
allow(completion_seconds_metric).to receive(:observe) let(:job_status) { :done }
allow(user_execution_seconds_metric).to receive(:observe) let(:labels) { { queue: :test } }
let(:labels_with_job_status) { { queue: :test, job_status: job_status } }
expect { |b| middleware.call(worker, {}, :test, &b) }.to yield_control.once let(:thread_cputime_before) { 1 }
end let(:thread_cputime_after) { 2 }
let(:thread_cputime_duration) { thread_cputime_after - thread_cputime_before }
it 'sets queue specific metrics' do let(:monotonic_time_before) { 11 }
labels = { queue: :test } let(:monotonic_time_after) { 20 }
labels_with_job_status = { queue: :test, job_status: :done } let(:monotonic_time_duration) { monotonic_time_after - monotonic_time_before }
allow(middleware).to receive(:get_thread_cputime).and_return(1, 3)
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(2, 3) let(:queue_duration_for_job) { 0.01 }
before do
allow(middleware).to receive(:get_thread_cputime).and_return(thread_cputime_before, thread_cputime_after)
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(monotonic_time_before, monotonic_time_after)
allow(Gitlab::InstrumentationHelper).to receive(:queue_duration_for_job).with(job).and_return(queue_duration_for_job)
expect(running_jobs_metric).to receive(:increment).with(labels, 1) expect(running_jobs_metric).to receive(:increment).with(labels, 1)
expect(running_jobs_metric).to receive(:increment).with(labels, -1) expect(running_jobs_metric).to receive(:increment).with(labels, -1)
expect(user_execution_seconds_metric).to receive(:observe).with(labels_with_job_status, 2)
expect(completion_seconds_metric).to receive(:observe).with(labels_with_job_status, 1)
middleware.call(worker, {}, :test) { nil } expect(queue_duration_seconds).to receive(:observe).with(labels, queue_duration_for_job) if queue_duration_for_job
expect(user_execution_seconds_metric).to receive(:observe).with(labels_with_job_status, thread_cputime_duration)
expect(completion_seconds_metric).to receive(:observe).with(labels_with_job_status, monotonic_time_duration)
end end
it 'ignore user execution when measured 0' do it 'yields block' do
allow(completion_seconds_metric).to receive(:observe) expect { |b| middleware.call(worker, job, :test, &b) }.to yield_control.once
allow(middleware).to receive(:get_thread_cputime).and_return(0, 0) end
it 'sets queue specific metrics' do
middleware.call(worker, job, :test) { nil }
end
context 'when job_duration is not available' do
let(:queue_duration_for_job) { nil }
expect(user_execution_seconds_metric).not_to receive(:observe) it 'does not set the queue_duration_seconds histogram' do
middleware.call(worker, job, :test) { nil }
end
end end
context 'when job is retried' do context 'when job is retried' do
it 'sets sidekiq_jobs_retried_total metric' do let(:job) { { 'retry_count' => 1 } }
allow(completion_seconds_metric).to receive(:observe)
expect(user_execution_seconds_metric).to receive(:observe)
it 'sets sidekiq_jobs_retried_total metric' do
expect(retried_total_metric).to receive(:increment) expect(retried_total_metric).to receive(:increment)
middleware.call(worker, { 'retry_count' => 1 }, :test) { nil } middleware.call(worker, job, :test) { nil }
end end
end end
context 'when error is raised' do context 'when error is raised' do
it 'sets sidekiq_jobs_failed_total and reraises' do let(:job_status) { :fail }
labels = { queue: :test }
labels_with_job_status = { queue: :test, job_status: :fail }
allow(middleware).to receive(:get_thread_cputime).and_return(1, 4)
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(2, 6)
expect(running_jobs_metric).to receive(:increment).with(labels, 1) it 'sets sidekiq_jobs_failed_total and reraises' do
expect(running_jobs_metric).to receive(:increment).with(labels, -1)
expect(failed_total_metric).to receive(:increment).with(labels, 1) expect(failed_total_metric).to receive(:increment).with(labels, 1)
expect(user_execution_seconds_metric).to receive(:observe).with(labels_with_job_status, 3)
expect(completion_seconds_metric).to receive(:observe).with(labels_with_job_status, 4)
expect { middleware.call(worker, {}, :test) { raise StandardError, "Failed" } }.to raise_error(StandardError, "Failed") expect { middleware.call(worker, job, :test) { raise StandardError, "Failed" } }.to raise_error(StandardError, "Failed")
end end
end end
end end
......
...@@ -57,21 +57,7 @@ describe Projects::ContainerRepository::DeleteTagsService do ...@@ -57,21 +57,7 @@ describe Projects::ContainerRepository::DeleteTagsService do
end end
end end
context 'with dummy tags disabled' do context 'with tags to delete' do
let(:tags) { %w[A Ba] }
before do
stub_feature_flags(container_registry_smart_delete: false)
end
it 'deletes tags one by one' do
expect_delete_tag('sha256:configA')
expect_delete_tag('sha256:configB')
is_expected.to include(status: :success)
end
end
context 'with dummy tags enabled' do
let(:tags) { %w[A Ba] } let(:tags) { %w[A Ba] }
it 'deletes the tags using a dummy image' do it 'deletes the tags using a dummy image' do
......
...@@ -8,6 +8,7 @@ describe 'profiles/show' do ...@@ -8,6 +8,7 @@ describe 'profiles/show' do
before do before do
assign(:user, user) assign(:user, user)
allow(controller).to receive(:current_user).and_return(user) allow(controller).to receive(:current_user).and_return(user)
allow(view).to receive(:experiment_enabled?)
end end
context 'when the profile page is opened' do context 'when the profile page is opened' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment